浏览器的SameSite策略

导读

本文可以帮助你解决以下问题

  • 想要了解一下SameSite属性(产生背景以及各个属性)
  • 种跨域cookie有问题,猜测可能是SameSite引起的,需要解决

CSRF攻击

  要了解SameSite产生的背景,我们先要了解一下CSRF攻击。如果你对此部分不敢兴趣,可以直接看SameSite部分。

什么是CSRF

  CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
  一个典型的CSRF攻击有着如下的流程:

*受害者登录a.com,并保留了登录凭证(Cookie)。
*攻击者引诱受害者访问了b.com。
*b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。
*a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
*a.com以受害者的名义执行了act=xx。
*攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。

  我先举一个CSRF攻击的例子吧,如果你对CSRF攻击还不太了解的话,可以先参考前端安全系列(二):如何防止CSRF攻击?这篇文章,再来看这个案例,我们此次的案例是模拟百度地图的收藏操作。

图一 未收藏串串火锅店
  我们搜索“吃闹热火锅串串”这家店铺,从上图我们可以看到,此时我们并没有收藏这家店铺。然后我们点击收藏操作,在chrome控制台分析一下发出的请求。
图二 请求 General部分

图三 请求 Request Headers部分

图四 请求 FormData部分

  很明显,这是一个post请求,貌似是用cookie来验证登陆信息的,并且content-type为application/x-www-form-urlencoded。那么正好,我们可以新建一个网站,利用post表单来模拟一下此次CSRF攻击请求。假设场景是:我在用自己的账号登陆了百度地图,然后闲的没事儿,去某个小说网站看了会儿小说,然后一回来,突然发现自己关注了这一家串串店铺,我很疑惑,我并没有点击收藏,但是为什么收藏了这家店铺呢?
图五 收藏了串串火锅店

  后来我查阅大量资料,最后得出结论,是这家小说网站存在问题,这家网站的html代码如下所示。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
</head>
<body>
<form action="https://map.baidu.com/?newmap=1&reqflag=pcmap&biz=1&from=webmap&da_par=direct&pcevaname=pc4.1&qt=fav&mode=add&type=favdata&lastver=1726297598" method="post">
  <input type="text" name="data" value='{"type":"10","sourceid":"aaf0ade9a5d29f9dc62ba16a","plateform":3,"fromapp":"百度地图","extdata":{"name":"吃闹热火锅串串","geoptx":"12962784.59","geopty":"4829524.68"}}'/>
  <input type="text" name="validate" value="58241"/>
  <input type="submit" value="Submit"/>
</form>
<div>
  小说《斗破苍穹》此处省略几百万字
</div>
<script>
  document.forms[0].submit();
</script>
</body>

  上面的html代码中,这个小说网站利用form表单的post请求发起了一个CSRF攻击,模拟了用户点击收藏的行为,并且骗过了服务端,成功收藏了这家店铺。
  具体过程如下:当用户登陆了map.baidu.com以后,map.baidu.com域名下会种上用户登陆信息的cookie,每次对map.baidu.com发起请求时,浏览器会默认带上这个cookie,服务端以cookie来校验请求发起人的身份。也就是说,小说网站对map.baidu.com这个域名发起请求,也会带上用户登陆信息cookie,再加上post表单并没有跨域限制,所以最后成功模拟了用户的收藏操作。


图六 请求图示

  这是不是很荒唐,如果你有兴趣去尝试一下的话,会发现很多网站其实也存在着这种安全问题,为了防止这种攻击行为,chrome很早就推出了cookie的SameSite属性来限制第三方cookie,但是大家伙都不理不睬。所以chrome干脆一不做二不休,在chrome80版本以后默认把cookie的SameSite属性设置为Lax,以此来对第三方cookie做一些限制,这样就能大大地降低被CSRF攻击的风险了。
  但是chrome也很头疼,因为此次强制更新SameSite的默认策略,势必会给不少使用第三方cookie的系统造成大麻烦,所以chrome在年前就在控制台给出了黄色警告,此次chrome更新SameSite默认策略对于这些有使用第三方cookie场景的系统,往往会造成登陆异常,系统崩溃等大问题。

SameSite属性介绍

  SameSite是cookie的一个属性,用来限制第三方Cookie。可以参考阮一峰老师的Cookie 的 SameSite 属性,或者直接看我画的下图。简单来说他有三个值:

  • Strict(禁止第三方 cookie)

  • Lax(稍微严格一点)

  • None (注意,cookie的SameSite设置为None以后,Secure需要同时设置为true,这意味着你的网站必须支持https)


    图七 SameSite策略

      Chrome80以后,cookie默认的SameSite策略是Lax。大家可以参考图一中的画出的SameSite为Lax时对第三方cookie的禁止情况,如果你有在POST表单,iframe,AJAX,Image这些场景中使用第三方cookie的情况,那么就需要注意了。

问题分析

  包括登陆在内的很多信息都是存储在Cookie当中,但是浏览器的默认策略是允许第三方网站发起的请求中携带cookie,这样就会很容易遭到CSRF攻击。
  Chrome浏览器从80版本开始更新默认的SameSite策略,新的策略为:在所有的 Cookie 中默认设置 SameSite=Lax 来屏蔽部分第三方 Cookie。
  如果你正在使用Chrome浏览器,没有上线也没有更新,但是某天突然遇到第三方请求的cookie没有种上(比如登陆cookie没有种上,导致用户登陆不上)的情况,那你可能就需要考虑是不是Chrome80以后正在实行的新版SameSite策略造成的问题了。

案例

图八 案例

  B站的html代码如下,iframe的src指向A站(SameSite.a.com)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
</head>
<body>
<iframe src="http://SameSite.a.com" frameborder="0"></iframe>
</body>

  比如目前我们的A网站是http://SameSite.a.com,A网站作为iframe嵌入在B网站http://test.b.com中。某天我们突然发现在B网站中加载A网站的iframe时我们发起请求来setCookie,但是cookie并没有种上。
  大致情况我们可以结合图二与图三来看,A站作为iframe嵌入在B站中,在B站中访问iframe(A站)时想要发起请求setCookie,但是失败。
  以登陆信息(图中为loginToken)举例,大多数系统在用户访问时如果登陆信息校验失败,就会发起登陆请求,登陆请求setCookie的同时又会重定向到系统,所以页面可能就会反复跳转刷新,进入死循环。

图九 setCookie失效

  如果出现第三方请求setCookie失效的情况,那么我们就可以使用以下操作来验证是否为SameSite的问题。
步骤如下:

  • Chrome浏览器地址栏输入 chrome://flags/

  • 找到:SameSite by default cookies、Cookies without SameSite must be secure

  • 设置上面这两项设置成 Disable,重启浏览器


    图十 chrome设置

  此时我们再次验证网页之前所存在的问题,如果问题解决了,说明你的浏览器确实存在SameSite策略的问题。

解决方法

  如果你依然想采用http域名的话,可以使用方案一;如果方案一不可行的话,就升级到https。

方案一:统一主域名

  将A网站与B网站的主域名统一,比如我们可以将A网站由SameSite.a.com变为SameSite.b.com,与B网站test.b.com保持一致。当然,如果采取这种方法的话,需要重新申请域名,并且原有的域名都需要重定向到新域名。


图十一 A站

方案二:升级https

图十二 升级HTTPS
SameSite设置

  A网站的请求中,cookie的设置应该带有Secure; SameSite=None这两个属性

域名升级https。

  你可能需要对项目进行整体改造,比如将所有http的请求替换为https。如果你的项目中引入了百度地图的js包,并且一直无法请求https的百度地图sdk,我的另一篇文章:引入百度地图js包的问题可能会帮你解决这个问题。

特别注意:
  • SameSite=None需要只对chrome80以上版本进行设置,原因在于chrome51-chrome66以及其他某些浏览器不接受SameSite=None 。
  • 如果网站存在http与https同时使用的情况,需要对此做兼容,只有https的时候才设置Secure;SameSite=None。原因在于当Secure设置为true时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证。
最后

  即使我们此次不设置SameSite为Strict或者Lax,我们也应该思考如何去预防CSRF攻击,因为SameSite设置为None的话,意味着第三方网站能发送携带cookie的请求,就如文章一开始举例的CSRF攻击一样。

小结

  我们首先介绍了SameSite属性产生的背景以及为何chrome要做此次SameSite默认策略的升级,然后讲解了SameSite对应的三个属性具体限制了哪些第三方cookie,最后举了一个chrome此次升级造成系统异常的案例,给出了具体的解决方案。
  chrome一直如此关注安全问题,逼迫着我们做系统改造升级,我们也应该反思,多做一些工作来提高我们系统的安全性。

参考文章

推荐阅读更多精彩内容