问题场景
在开发配合外部系统的子系统过程中,外部系统需要实现”一网通管”并通过单点登录方式实现免登录。当前实现是外部系统传入sso_token信息,子系统返回重定向response,用户在新标签页中可以正常访问子系统的数据看板页面。
但当外部系统通过iframe方式嵌入子系统时,却无法正常查看数据看板页面,而是跳转到了登录页面。即使输入密码登录后仍然无法进入系统。需要实现外部系统在iframe中嵌入子系统时,子系统仍能正常免登录查看数据看板页面。
问题分析
通过浏览器开发者工具分析请求信息:
打开新标签页跳转数据看板页面的请求信息正常:
但在iframe中跳转数据看板页面时,发现cookie无法正常设置,请求没有携带cookie。这是因为Set-Cookie标头未指定”SameSite”属性,默认为”SameSite=Lax”,而该cookie被浏览器屏蔽,因为它来自一个跨站响应,且不是顶级导航操作的响应。要使cookie能在跨站场景下使用,必须设置”SameSite=None”。
这导致请求没有携带任何用户信息,后台判断为未登录状态后跳转到登录页面:
同样,在iframe中输入密码登录时也存在相同问题,无法设置cookie:
Cookie属性详解
基本属性
domain=domain(例如,example.com或subdomain.example.com):指定cookie将发送到的主机。如果未指定,则默认为当前文档位置的主机部分,且cookie在子域上不可用。如果指定了域,则始终包含子域。注意:该域必须与JavaScript源的域匹配。
expires=date-in-GMTString-format:指定cookie的到期日期。如果既没有指定expires也没有指定max-age,则cookie将在会话结束时过期。
警告:出于用户隐私考虑,Web应用程序应在一定超时后使Cookie数据无效,而不是依赖浏览器来执行此操作。
max-age=max-age-in-seconds:指定cookie的最长期限(以秒为单位),例如606024*365=31536000表示一年。
partitioned:表示cookie应该使用分区存储来存储。有关更多详细信息,请参阅具有独立分区状态的Cookie (CHIPS)。
path=path:指定请求URL中必须存在的路径,以便浏览器发送Cookie标头(例如”/“、”/mydir”)。如果未指定,则默认为当前文档位置的当前路径。
samesite:阻止浏览器随跨站点请求发送此cookie。可能的值包括:
- lax:发送同站点请求和顶级导航GET请求的cookie。对于用户跟踪已经足够,但可以防止许多CSRF攻击。这是现代浏览器中的默认值。
- strict:阻止浏览器在所有跨站点浏览上下文中将cookie发送到目标站点。
- none:明确表明不会应用任何限制。该cookie将在所有请求中发送 - 跨站点和同站点。
httponly:如果在Cookie中设置了”HttpOnly”属性,则通过程序(JS脚本、Applet等)将无法读取到Cookie信息,能有效防止XSS攻击。
secure:指定cookie只能通过安全协议(HTTPS)传输。
Cookie前缀
一些用户代理实现支持以下cookie前缀:
__Secure-:向浏览器发出信号,表明它应该仅在通过安全通道传输的请求中包含cookie。
__Host-:向浏览器发出信号,表明除了仅使用来自安全来源的cookie的限制之外,cookie的范围还仅限于服务器传递的路径属性。它还表明域属性不能存在,这会阻止cookie被发送到其他域。

解决方案
在外部系统携带sso信息请求过来时,返回response时需要添加Set-Cookie响应头,设置SameSite属性为None,并搭配Secure属性使用:
1 | Set-Cookie: sessionid=abc123; SameSite=None; Secure |

服务端实现示例
以常见的Web框架为例,展示如何正确设置Cookie:
Django示例
1 | response = HttpResponseRedirect('/dashboard/') |
Flask示例
1 | from flask import make_response |
Node.js Express示例
1 | res.cookie('sessionid', 'abc123', { |
注意事项
SameSite=None必须与Secure配合使用:当设置SameSite=None时,必须同时设置Secure属性,否则会被浏览器忽略。
HTTPS环境要求:由于需要设置Secure属性,整个系统必须运行在HTTPS环境下。
浏览器兼容性:较老的浏览器可能不支持SameSite=None属性,需要进行兼容性处理。
安全性考虑:
- 使用HttpOnly属性防止XSS攻击
- 设置适当的过期时间
- 使用安全的会话管理机制
最佳实践总结
- 在跨站嵌入场景中,必须显式设置SameSite=None和Secure属性
- 确保整个应用运行在HTTPS环境下
- 合理设置cookie的过期时间和安全属性
- 在服务端正确处理跨域单点登录逻辑
- 充分测试各种浏览器环境下的兼容性