为何Django会话Cookie在iPhone、iPad移动端Safari重开浏览器后会丢失?
问题核心原因与配置风险说明
为何仅iOS 14.3版本移动端Safari出现会话丢失
这个版本的Safari在更新智能反追踪(ITP)策略时存在已知逻辑bug:
- 未显式标记为
SameSite=None的会话Cookie(也就是Django默认的SameSite=Lax配置),会在Safari进程被系统完全终止、重新打开浏览器时,被误判为跨站追踪类临时Cookie,直接被浏览器的存储清理流程删除,无法保留原有会话状态 - 桌面端Safari的反追踪逻辑独立运行,没有搭载这个强制清理分支,所以同版本下无法复现该异常
- 这个bug在后续iOS 14.4版本的Safari补丁中被正式修复,但存量的14.3版本设备仍会触发该问题
- 临时把
SESSION_COOKIE_SAMESITE设为None之所以能解决问题,是因为浏览器会把显式声明SameSite=None的Cookie归类为站点明确需要保留的合法会话存储,不会触发上述误清理逻辑,哪怕这个站点根本没有跨站携带Cookie的需求
注:该配置项的值必须用英文单引号包裹,使用中文引号会直接触发Python语法错误,导致Django配置加载失败
设置SESSION_COOKIE_SAMESITE = 'None'的实际风险等级
需要明确的是,该配置本身不会直接引入高危安全漏洞,实际风险属于中低等级,完全可通过配套配置对冲:
- 浏览器端的SameSite属性本质是跨站请求携带Cookie的规则开关,不是服务端的核心安全防线。Django自带的CSRF中间件是独立的服务端校验层,不依赖SameSite属性运行,不会因为开启None就失去防护能力
- 真实暴露的风险包含两部分:
- 浏览器兜底防护失效:默认
SameSite=Lax时,浏览器会直接拦截所有跨站发起的POST/PUT/DELETE等非安全方法请求,不会携带会话Cookie,相当于帮应用挡掉了绝大多数无CSRF令牌的跨站攻击。开启None后,这层浏览器兜底会消失,所有跨站请求都会携带会话Cookie,一旦站点存在遗漏CSRF校验的接口,就会被跨站攻击利用 - 第三方内容读取风险:按照规范,标记为
None的Cookie会在所有跨站请求中携带,如果站点嵌入了第三方iframe、广告脚本等内容,这些内容会获得读取该Cookie的权限。但因为Django默认开启了SESSION_COOKIE_HTTPONLY = True,前端脚本无法直接读取Cookie值,不会造成会话凭据泄露
- 浏览器兜底防护失效:默认
- 额外的前置要求:按照浏览器规范,
SameSite=None的Cookie必须同时开启Secure属性,否则会被现代浏览器直接拒绝存储,导致配置完全不生效。
过渡阶段的优化配置方案
为了降低配置调整带来的额外风险,可参考以下方案:
- 配套开启
SESSION_COOKIE_SECURE = True,确保Cookie能被iOS端Safari正常存储 - 全站排查所有接口的CSRF校验逻辑,确保没有遗漏未加防护的接口,替代原来依赖SameSite=Lax的浏览器兜底防护
- 长期可基于设备UA做动态配置:仅对识别到的iOS 14.3版本移动端Safari设备返回
SameSite=None的会话Cookie,其他所有客户端保持默认SameSite=Lax配置,最小化安全暴露面
内容的提问来源于stack exchange,提问作者TomRavn
相关产品推荐
相关产品推荐

