Django中如何防止攻击者跨域复用会话获取权限?
解决跨子域名会话伪造的Django方案
不用部署两个独立实例或分开数据库,你可以通过以下几种方式阻止这种非法访问:
一、给会话绑定请求域名+自定义中间件验证
这个思路是让每个会话和用户登录时的域名绑定,后续请求如果域名不匹配就拒绝访问:
- 登录时记录绑定域名
在用户登录的逻辑里(比如自定义登录视图,或者用user_logged_in信号),把当前请求的域名存入会话:from django.contrib.auth.signals import user_logged_in from django.dispatch import receiver @receiver(user_logged_in) def bind_session_to_domain(sender, request, user, **kwargs): request.session['allowed_domain'] = request.get_host() - 编写验证中间件
创建一个中间件,在每次请求时检查已登录用户的请求域名是否和会话绑定的域名一致:
记得把这个中间件加到# myapp/middleware.py from django.contrib.auth import logout from django.http import HttpResponseForbidden class DomainSessionValidationMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.user.is_authenticated: allowed_domain = request.session.get('allowed_domain') if allowed_domain and allowed_domain != request.get_host(): logout(request) return HttpResponseForbidden("Invalid domain for session") response = self.get_response(request) return responsesettings.py的MIDDLEWARE列表里,放在AuthenticationMiddleware之后。
二、给两个子域名配置不同的会话Cookie名称
让两个子域名使用不同的会话Cookie名称,这样复制过来的Cookie会被Django忽略:
你可以通过中间件动态调整会话Cookie的名称,根据请求域名切换:
# myapp/middleware.py class DynamicSessionCookieNameMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): host = request.get_host() if host == 'public.example.com': request.session.cookie_name = 'public_sessionid' elif host == 'private.example.com': request.session.cookie_name = 'private_sessionid' response = self.get_response(request) # 确保响应里的Cookie名称也对应 if 'sessionid' in response.cookies: response.cookies[request.session.cookie_name] = response.cookies.pop('sessionid') return response
同样把这个中间件加到MIDDLEWARE里,注意顺序要在SessionMiddleware之后。
三、给会话数据添加域名签名验证
用Django的签名模块给域名做签名,验证时确保当前请求域名和签名一致:
- 登录时生成域名签名
from django.core.signing import dumps from django.contrib.auth.signals import user_logged_in from django.dispatch import receiver @receiver(user_logged_in) def sign_session_domain(sender, request, user, **kwargs): domain_signature = dumps(request.get_host(), salt='domain_session_salt') request.session['domain_signature'] = domain_signature - 中间件验证签名
from django.core.signing import loads, BadSignature from django.contrib.auth import logout from django.http import HttpResponseForbidden class SignedDomainSessionMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.user.is_authenticated: signature = request.session.get('domain_signature') if signature: try: allowed_domain = loads(signature, salt='domain_session_salt') if allowed_domain != request.get_host(): logout(request) return HttpResponseForbidden("Session domain mismatch") except BadSignature: logout(request) return HttpResponseForbidden("Invalid session signature") response = self.get_response(request) return response
这些方案都不需要拆分项目实例或数据库,只需要添加少量代码就能实现会话的域名隔离,阻止手动复制Cookie的跨域访问。
内容的提问来源于stack exchange,提问作者Sævar
相关产品推荐
相关产品推荐

