原文链接:iframe页面二次登录问题
生产问题
问题背景
由于历史原因,公司内部系统有一些页面是基于iframe嵌入的其他系统的页面,之前一直运行正常,最近不知什么原因接连出现访问所有iframe页面时提示需要登录的情况,并且点击iframe页面的登录按钮时会出现页面闪一下,没有任何跳转的现象。
问题显而易见,怎么解决呢?透过现象看本质,旁边业务还在催问什么时候能恢复,只能硬着头皮一步步分析着看。
首先,这些系统都接入了公司内部的sso服务,主体系统A已经成功登录的情况下,内嵌系统B还需要二次登录,肯定是相关鉴权未通过。
而整个鉴权信息都是基于COOKIE机制来完成的,所以借此机会先回顾一下COOKIE相关知识。
原因定位
跟踪网络请求发现,当请求内嵌页面时,首先会发起SSO认证请求https://sso.xxx.com/authentication/login?sevice=http://b.xxx.com
,这个请求的scheme是HTTPS,而主系统A没有升级HTTPS, 所以B系统发起SSO认证时属于cross-site
情形,无法携带此前生成的用户身份COOKIE信息,用户鉴权失败。
当点击内嵌页面中的登录按钮时,由于此时发起的请求仍然是cross-site
的,所以COOKIE始终无法写入,就会出现页面闪一下,但并未登录的情况。
COOKIE
HTTP COOKIE是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器发起请求时被携带并发送到服务器上。
COOKIE主要用于以下三个方面:
- 会话状态管理,如用户登录状态、购物车、游戏分数等。
- 个性化设置,如用户自定义设置,主题等。
- 浏览器行为跟踪,如跟踪分析用户行为等。
第三方COOKIE
COOKIE与域关联。如果此域与您所在页面的域相同,则该COOKIE称为第一方COOKIE(first-party COOKIE)。如果域不同,则它是第三方COOKIE(thirty-patry COOKIE)。第三方COOKIE是由第三方网站引导发出的,可以用于CSRF攻击以及用户行为追踪。
限制访问COOKIE
通过以下方式可以确保COOKIE被安全发送,不会被意外的参与者或脚本访问:
-
Secure
属性:标记为Secure
的COOKIE只能通过被HTTPS协议加密过的请求发送给服务端,可以借此来预防中间人攻击从 Chrome 52 和 Firefox 52 开始,不安全的站点(
http:
)无法使用COOKIE的Secure
标记。 -
HttpOnly
属性:Javascript的Document.COOKIE
API无法访问带有HttpOnly属性的COOKIE,此类COOKIE仅作用于服务器。例如,持久化服务器会话的COOKIE不需要对Javascript可用,因此可以设置HttpOnly
属性。可以借此来缓解XSS攻击。
Set-COOKIE: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
COOKIE作用域
通过Domain
和Path
标识来定义COOKIE的作用域:即允许COOKIE应该发送给哪些URL。
Domain属性
指定哪些主机可以接受COOKIE。默认为origin,不包含子域名。如果指定了Domain
,则一般包含子域名。
当前大多数浏览器遵循 RFC 6265,设置 Domain 时 不需要加前导点。浏览器不遵循该规范,则需要加前导点,例如:Domain=.mozilla.org
Path属性
指定主机下哪些路径可以接受COOKIE(该URL路径必须存在于请求URL中)。以字符%X2F("/")作为路径分隔符,子路径也会被匹配。
例如,设置Path=/docs,以下地址都会匹配:
- /docs
- /docs/web/
- /docs/web/HTTP
SameSite属性
SameSite
属性允许服务器要求某个COOKIE在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
Set-COOKIE: key=value; SameSite=Strict
SameSite
取值有以下三种:
-
None
:浏览器会在同站请求、跨站请求下继续发送COOKIE,不区分大小写。大多数网站已经将
SameSite
的默认值设置为Lax
,此时如果网站想要关闭SameSite
属性,必须在将SameSite
属性设置为None
的同时设置Secure
属性,否则无效。
// 该设置无效
Set-COOKIE: widget_session=abc123; SameSite=None
// 有效设置
Set-COOKIE: widget_session=abc123; SameSite=None; Secure
Strict
浏览器只在访问相同站点时发送COOKIE。Lax
:与Strict
类似,但用户从外部站点导航至目标网址的Get请求除外(链接、预加载请求以及GET表单)。
请求类型 | 示例 | 正常情况 | Lax |
---|---|---|---|
链接 |
|
发送 COOKIE | 发送 COOKIE |
预加载 |
|
发送 COOKIE | 发送 COOKIE |
GET 表单 |
|
发送 COOKIE | 发送 COOKIE |
POST 表单 |
|
发送 COOKIE | 不发送 |
iframe |
|
发送 COOKIE | 不发送 |
AJAX | $.get("...") |
发送 COOKIE | 不发送 |
Image |
|
发送 COOKIE | 不发送 |
same-origin和same-site
同源(same-origin
)、同站点(same-site
)是两个随处可见的概念,但是这两个概念经常容易被混淆。
Origin
首先来看一下origin的定义是什么。scheme + hostname + port
构成的整体叫做origin,例如https://www.example.com/443/foo
的origin是https://www.example.com/443
。
具有相同的scheme、hostname以及port的站点被视为同源站点,否则视为跨域(cross-origin
)站点.
Site
学习site之前,先来了解一下什么是TLD、eTLD。
TLD
TLD全称叫做顶级域名(Top-Level-Domain),比如我们经常看到的com、cn、io之类的,都属于顶级域名。
TLD有一个记录列表,这个列表叫做Root Zone Database,里面记录了所有的顶级域名。需要注意的是,顶级域名不一定都是单词很短且只有一级的域名。
如上图,TLD和它之前部分的域构成的整体叫做"site",比如https://www.example.com:443/foo
,"site" 是example.com
。
eTLD
除了顶级域名之外,还有一种叫做eTLD(effective Top-Level-Domain)的东西,它表示的是有效顶级域名。
例如.io
是一个TLD,而像.github.io
,是一个开放给用户的用于搭建个人网站的一个域。比如现在有以下三个网站:
http://zhangsan.github.io
http://lisi.github.io
http://wangwu.github.io
我们判断是否是同一个站点,通常是采用顶级域名+二级域名来判断,这里如果直接用.io
这个TLD来识别,就会认为这三个网站是同一个站点,COOKIE可以共享。
显然这是有问题的,因此需要引入eTLD的概念,将.io
与github
合起来的.github.io
注册为一个"effective TLDs"。将eTLD+1整体视为网站的站点名称,这样一来,上述三个网址表示的就是不同的网站,COOKIE就会相互隔离。
eTLD信息在Public Suffix List列表中定义,可以通过publicsuffix.org/list查询域名是否为有效顶级域名。
schemeful same-site
关于"same-site的定义一直在不断演变,由此出现将URL Scheme看作是site的一部分的这种策略。基于这种策略可以避免网站遭受基于HTTP协议的一些攻击。
在schemeful same-site规则下,由于scheme不同,http://www.example.com
和https://www.example.com
被认为是不同的站点。
如何判断
Chrome浏览器发送请求时会携带名为Sec-Fetch-Site
的Header信息,根据该Header的取值可以判断当前请求是否同源、是否跨站(schemeful-same-site
未被记录在Sec-Fetch-Site
中)。
Sec-Fetch-Site
取值如下:
cross-site
same-site
same-origin
none