场景
我们的页面使用 iframe
集成了第三方的页面,在 chrome 84
版本中页面白屏无法正常显示,部分 iframe 页面上显示错误码 401
未认证。
在开发者工具 Application 中可以查看到 iframe 域下的 cookie 是存在的,但是发出的请求中并没有带上 cookie,所以导致认证失败,获取不到资源。
原因
从 Chrome 51开始,浏览器的 Cookie 新增加了一个 SameSite
属性,用来防止 CSRF
攻击和用户追踪。
该设置当前默认是关闭的,但在 Chrome 80
之后,该功能默认已开启。
SameSite 属性有三个属性值
Strict
:完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 CookieLax
:大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。iframe
、AJAX
、Image
是不会发送第三方 cookie 的。None
设置了 Strict
或 Lax
以后,基本就杜绝了 CSRF
攻击。当然,前提是用户浏览器支持 SameSite
属性。
在 iframe 中,即使 iframe 页面和 iframe 的服务器不存在跨域问题,但是当 iframe 发出请求时,iframe 的父级页面也就是当前网页的 URL 和请求目标并不同源,所以请求想要携带的 iframe 域下的 cookie 属于第三方 cookie,浏览器默认会禁止其携带。
下面我们把场景简单复现出来。
iframe 页面
父级容器页面
iframe 页面运行在 http://localhost:5000/test
,我们在 domain = localhost
的 cookie 里手动写入了 a_id=xiaolong
可以看到,页面没有嵌入时,请求可以正常携带 cookie
domain 设置时是不能设置端口号的
父级容器页面运行在 http://192.168.0.105:62871/home
,此时 iframe 发出的请求并没有携带 cookie
然后我们又把父级容器的 URL 改成 http://localhost:62871/home
,cookie 就可以携带了
此时我们应该注意到,父级页面和 iframe 页面的域的端口号并不相同,但是域名相同也能携带 cookie
所以准确的说,cookie 不能跨域名(跨站点),而不是 cookie 不能跨域,同一站点下的不同端口号的 cookie 是共享的。
localstorage
对于 localstorage
,则是完全遵循跨域的特性, iframe 和父级容器页面即使域名相同,端口号不同,也是不会共享的。并且 iframe 中也可以正常设置和使用 localstorage。
解决
可以打开 chrome://flags/#same-site-by-default-cookies
手动将其暂时关闭 SameSite,但这并不是解决方案。
服务端
set-cookie
的时候,设置 SameSite 为 None,同时设置Secure
。且需要将后端服务域名必须使用 https 协议访问。SameSite 本来就是为了保障安全性的设置,现在又把其关闭。。。🤔部署到同一个二级域名下,设置 domain,就不存在 cookie 跨域了
使用
Nginx
或其他网关工具进行Proxy
操作使用 token(jwt) 代替 cookie 方式作验证。
参考:
作者: 熊枭龙