4

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

攻击原理

CSRF是如何发生的呢,我们以网银转账为例进行说明。
image.png

  1. 首选用户通过浏览器访问网银系统
  2. 用户在网银登录后,浏览器会把用户session_id保存在浏览器Cookie中
  3. 此时用户在同一个浏览器中访问了第三方网站
  4. 第三方网站诱导用户访问了网页转账的链接
  5. 由于用户在网银系统已经登录了,浏览器访问网银转账链接时,会带上用户在网银的Cookie信息
  6. 网银系统根据用户提交Cookie中的session_id,以为用户本人发起了转账操作,于是执行转账业务。

至此,在用户不知情的情况下,网银执行了转账业务,这就是跨站(第三方站点的发起请求)请求伪造(非用户发起的请求)的基本攻击原理。

触发伪造情况包括多种方式,常见为如下几种方式:

  1. 第三方页面把图片地址设置为攻击链接,访问页面就自动触发请求。
<img src="http://bank.com/transfer?account=lisi&amount=100">
  1. 第三方页面上构造一个form表单,访问页面时,表单通过JavaScript自动提交。
<form action="http://bank.com/transfer">
    <input type="hidden" name="account" value="lisi">
    <input type="hidden" name="amount" value="100">
</form>
<script>
form[0].submit();
</script>
  1. 第三方页面上构造一个链接,诱导用户点击触发请求。
<a href="http://bank.com/transfer?account=lisi&amount=100">你想象不到的精彩,点我查看</a>

防范措施

根据CSRF攻击的特点,要成功攻击,必须满足几个条件。

  1. 用户在被攻击的系统中登录了。
  2. 用户在第三方系统触发了对被攻击系统的请求,而被攻击服务器无法识别此请求来源。

针对第一个条件,防范措施包括:

  1. 对重要的操作进行二次认证,防止操作在后台自动执行。
  2. 设置适当的会话超时时间,防止用户离开后,其他用户在同一个浏览器中操作。
  3. 养成良好的习惯,离席锁屏。

针对第二个条件,是我们从技术层面要重点防范的,可选的防范措施包括:

  1. 语义一致性:良好的编程习惯,操作类请求,必须使用POST,GET只用于浏览类请求。
  2. 阻止外域访问

    1. 同源检测:服务器端通过请求的Origin Header和Referer Header,判断请求的来源。
    2. Samesite Cookie:控制只有同域(子域)能访问Cookie。
  3. 随机数一致性检测

    1. CSRF Token:用户登录后,生成随机值csrf_token,用户提交的操作类(POST)请求中,提交的表单中携带csrf_token,服务器端判断csrf_token是否正确。
    2. 双重Cookie验证:Cookie中保存csrf_token,用户提交表单中也携带csrf_token,服务器端判断两个值是否一致。

上述几条防范措施,语义一致性是推荐措施,虽然它不能防范CSRF攻击,但是可以减少防范的范围,只对POST请求进行防范。剩下的四种防范方法中,CSRF Token是最成熟、使用最广泛的方法,建议采用。

同源检测

HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于bank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于bank.com之下,这时候服务器就能识别出恶意的访问。

这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。

CSRF大多数情况下来自第三方域名,但并不能排除本域发起。如果攻击者有权限在本域发布评论(含链接、图片等,统称UGC),那么它可以直接在本域发起攻击,这种情况下同源策略无法达到防护的作用。

Samesite Cookie

Chrome 51开始,浏览器的Cookie新增加了一个SameSite属性,用来防止CSRF攻击和用户追踪。Samesite有三个可选值,分别为Strict、Lax、None。

  • Strict:最严格模式,完全禁止第三方Cookie,跨站点访问时,任何情况下都不会发送Cookie。换言之,只有当前网页的 URL与请求目标一致,才会带上Cookie。

此方式虽然安全,但是存在严重的易用性问题,用户从第三方页面访问一个已登录的系统时,由于未携带Cookie,总是需要重新登录。

  • Lax:Chrome默认模式,对于从第三方站点以link标签a标签GET形式的Form提交这三种方式访问目标系统时,会带上目标系统的Cookie,对于其他方式,如 POST形式的Form提交AJAX形式的GETimg的src访问目标系统时,不到Cookie。
  • None:原始方式,任何情况都提交目标系统的Cookie。

由于Samesite是Google提出来的,其他浏览器目前并未普及,存在兼容性问题,目前不推荐使用。

CSRF Token

由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在Cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再运行CSRF攻击。这种数据通常是Form表单中的一个数据项。服务器将其生成并附加在Form中,其内容是一个伪随机数。当客户端通过Form提交请求时,这个伪随机数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪随机数的值,服务端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

<form method="post">
    <input type="hidden" name="csrf_token" value="7CD2di7JKP1P3qmLlkPt" />
</form>

CSRF Token需要服务器端对提交的请求进行验证,一般CSRF Token以Session参数的形式保存在服务器端。对于分布式应用,使用分布式Session来管理的难道比较大,可以考虑把Token保存在Redis等分布式缓存中。

双重Cookie验证

上述CSRF Token方式,需要在服务器上保存Token值,并对请求参数进行校验,增加了服务器端的复杂度。双重Cookie验证的原理是在Cookie中保存Token值,同时在Form表单中也提供该值,请求提交时,Cookie和Form表单中的Token同时提交,服务器端只需要对请求中的两个参数进行校验即可,省去了在服务器端维护Token的步骤。

由于Cookie的安全限制,只能在本域名或子域名下访问到Cookie值,兄弟子域名无法访问到,如a.bank.com域名下的Cookie,只能被a.bank.comsub.a.bank.com访问,无法被b.bank.com访问。对于分布式应用,可能需要在多个子域名中提交请求,所以一般需要把Cookie保存在根域名bank.com中。不过此方式存在安全风险,如果任何一个子域下的页面存在XSS攻击,可导致根域名下的Cookie被篡改,Token可被攻击者任意修改,导致安全措施失效。

参考材料

  1. 前端安全系列(二):如何防止CSRF攻击?

乘着风
107 声望12 粉丝

五岁时,妈妈告诉我,人生的关键在于快乐。上学后,人们问我长大了要做什么,我写下“快乐”。他们告诉我,我理解错了题目,我告诉他们,他们理解错了人生。——约翰·列侬