
本文详解如何在 Django 中通过正确配置 SESSION_COOKIE_DOMAIN 避免 Cookie 被错误共享至无关子域,重点解决以 .dev.fox.com 为域时导致 abc.dev.fox.com 等非目标子域意外获取 Cookie 的问题,并提供动态域名设置方案。
本文详解如何在 django 中通过正确配置 `session_cookie_domain` 避免 cookie 被错误共享至无关子域,重点解决以 `.dev.fox.com` 为域时导致 `abc.dev.fox.com` 等非目标子域意外获取 cookie 的问题,并提供动态域名设置方案。
在 Web 应用中,Cookie 的作用域(Domain)直接影响其安全性与隔离性。Django 默认使用 SESSION_COOKIE_DOMAIN 控制会话 Cookie 的生效范围。一个常见误区是:为支持多子域而配置带前导点号(.)的域名(如 '.dev.fox.com'),这实际会触发浏览器的「宽松匹配规则」——即该 Cookie 将被发送至所有以 dev.fox.com 为后缀的主机名(包括 app.dev.fox.com、api.dev.fox.com,甚至意外匹配的 xyz.dev.fox.com 或 abc.dev.fox.com)。这不仅违背最小权限原则,更可能引发身份越权、会话泄露等安全风险。
✅ 正确做法:显式指定精确域名,禁用通配匹配
关键原则是:移除前导点号(.)。只有不带点号的完整域名才会被浏览器严格匹配,从而实现子域级隔离:
# ❌ 错误:开启通配,导致所有 *.dev.fox.com 子域共享 Cookie SESSION_COOKIE_DOMAIN = '.dev.fox.com' # ✅ 正确:仅对指定域名生效(严格匹配) SESSION_COOKIE_DOMAIN = 'app.dev.fox.com' # 仅 app.dev.fox.com 可读写 # 或 SESSION_COOKIE_DOMAIN = 'dev.fox.com' # 仅 dev.fox.com 主域有效(不含子域)
⚠️ 注意:dev.fox.com 与 .dev.fox.com 在语义上截然不同。前者是具体主域;后者是「以 dev.fox.com 结尾的所有域名」的通配符表示。Django 文档明确指出:带前导点号的值将被解释为「该域及其所有子域」,这是 RFC 6265 的标准行为,而非 Django 特有逻辑。
? 动态设置 Cookie 域:适配多入口场景
当应用需支持多个独立子域(如 app.dev.fox.com、admin.dev.fox.com、abc.dev.fox.com)且要求各子域 Cookie 完全隔离时,静态配置不再适用。此时应结合请求 Host 头动态生成 SESSION_COOKIE_DOMAIN。
推荐在 settings.py 中覆盖默认行为,或在中间件中精细化控制:
提供使用 django-unfold (v0.56+) 构建高级 Django 管理界面的专业指导。当用户提及 django-unfold、unfold admin 等内容时使用此技能。
# settings.py
import os
# 启用动态域解析(仅当需要时启用)
SESSION_COOKIE_DOMAIN = None # 关键:设为 None,交由响应阶段动态控制
# 或在自定义中间件中(推荐用于精细控制)
# middleware.py
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
class DynamicSessionDomainMiddleware(MiddlewareMixin):
def process_response(self, request, response):
host = request.get_host().split(':')[0] # 忽略端口
# 白名单校验,防止 Host 注入攻击
allowed_domains = {
'app.dev.fox.com',
'admin.dev.fox.com',
'abc.dev.fox.com',
}
if host in allowed_domains:
response.set_cookie(
settings.SESSION_COOKIE_NAME,
request.session.session_key,
domain=host,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or False,
httponly=settings.SESSION_COOKIE_HTTPONLY,
samesite=settings.SESSION_COOKIE_SAMESITE,
)
return response
同时,在 settings.py 中确保关闭自动域推导:
# settings.py SESSION_COOKIE_DOMAIN = None SESSION_SAVE_EVERY_REQUEST = True # 确保每次请求刷新 Cookie(含新 domain)
? 安全提示:动态设置 Domain 时,务必对 request.get_host() 进行白名单校验。直接反射 Host 头可能导致 Cookie 被注入到恶意域名(如 attacker.com),构成严重安全漏洞。
? 补充说明:Firebase Token 场景下的注意事项
由于您提到 Token 来源于 Firebase Authentication,需额外注意:Firebase SDK 默认不会自动设置 Cookie,其 ID Token 通常由前端通过 getIdToken() 获取并手动携带至后端(如放入 Authorization Header)。因此,Django 的 SESSION_COOKIE_DOMAIN 仅影响 Django 自身会话 Cookie,不影响 Firebase Token 的传输逻辑。若需将 Firebase Token 绑定到服务端会话,请确保:
- 后端验证 Token 后,仅在当前请求 Host 对应的精确域名下写入 Django Session Cookie;
- 前端调用 Firebase 登录后,避免跨域发起会话创建请求(例如从 abc.dev.fox.com 登录却向 app.dev.fox.com 发起 /login/ 请求)。
✅ 总结
| 目标 | 推荐方案 |
|---|---|
| 完全隔离子域 Cookie | SESSION_COOKIE_DOMAIN = 'exact.host.name'(无前导点) |
| 支持多个互不信任的子域 | 使用中间件 + Host 白名单动态设置 Cookie Domain |
| 兼顾安全性与灵活性 | 始终校验 Host 头、禁用通配、启用 SameSite=Lax/Strict |
遵循以上实践,即可精准控制 Cookie 作用域,实现子域间的身份隔离,既满足多环境部署需求,又筑牢安全防线。










