开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情
一、简介
跨站请求伪造攻击,英文全称是Cross-site request forgery,简称为CSRF。
攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。
CSRF 攻击的本质:本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
如何攻击
从简介中我们得知,攻击者需要诱导用户进入一个第三方网站。
- 受害者登录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攻击,要同时满足2各条件
- 登录成功网站A,并本地产生了Cookie
- 未退出A网站的同时,访问危险的B.com
总结来说天时地利人和。
二、CSRF攻击分类
2.1 GET 类型的 CSRF
GET 类型的 CSRF 利用非常简单,只需要一个 HTTP 请求,一般这样使用
假设:网站A,银行账户转行,通过GET请求来完成
危险网站B,其中有一段代码
<img src="http://mybank.example/transfer?account=ying&for=hacker&money=10000">
若这时候访问了网站B,黑客将转账的请求接口隐藏在 img 标签内,欺骗浏览器这是一张图片资源。当该页面被加载时,浏览器会自动发起 img 的资源请求,如果服务器没有对该请求做判断的话,那么服务器就会认为该请求是一个转账请求,于是用户账户上的 10000 就被转移到黑客的账户上去了。
2.2 POST类型的 CSRF
还以上面的转账为例子进行说明
<form action="http://mybank.example/transfer" method=POST>
<input type="hidden" name="account" value="ying" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="for" value="hacker" />
</form>
<script>
document.forms[0].submit();
</script>
若访问了该页面,表单会自定提交。相当于进行了一次POST请求。
POST类型的攻击通常比GET要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许POST上面。
2.3 链接类型的CSRF
这点通俗来说,就是点击了危险网站的链接。通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招。
<a href="http://mybank.example/transfer?account=ying&for=hacker&money=10000" taget="_blank">
惊喜惊喜,快来领取!!
<a/>
三、防御
3.1 同源检测
CSRF大多来自第三方网站,那么我们就直接禁止外域(或者不受信任的域名)对我们发起请求。
在HTTP协议中,每一个异步请求都会携带两个Header,用于标记来源域名:
- Origin Header
- Referer Header
这两个Header在浏览器发起请求时,大多数情况会自动带上,并且不能由前端自定义内容。 服务器可以通过解析这两个Header中的域名,确定请求的来源域。
使用Origin Header确定来源域名
在部分与CSRF有关的请求中,请求的Header中会携带Origin字段。字段内包含请求的域名(不包含path及query),如果Origin存在,那么直接使用Origin中的字段确认来源域名就可以。
但是会有2种特殊情况,Origin是不存在的:
- IE11同源策略: IE 11 不会在跨站CORS请求上添加Origin标头,Referer头将仍然是唯一的标识。最根本原因是因为IE 11对同源的定义和其他浏览器有不同。
- 302重定向: 在302重定向之后Origin不包含在重定向的请求中,因为Origin可能会被认为是其他来源的敏感信息。对于302重定向的情况来说都是定向到新的服务器上的URL,因此浏览器不想将Origin泄漏到新的服务器上。
使用Referer Header确定来源域名
根据HTTP协议,在HTTP头中有一个字段叫Referer,记录了该HTTP请求的来源地址。
这种方法并非万无一失,Referer的值是由浏览器提供的,虽然HTTP协议上有明确的要求,但是每个浏览器对于Referer的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不是很安全。在部分情况下,攻击者可以隐藏,甚至修改自己请求的Referer。
在以下情况下Referer没有或者不可信:
- IE6、7下使用window.location.href=url进行界面的跳转,会丢失Referer。
- IE6、7下使用window.open,也会缺失Referer。
- HTTPS页面跳转到HTTP页面,所有浏览器Referer都丢失。
- 点击Flash上到达另外一个网站的时候,Referer的情况就比较杂乱,不太可信
因此,服务器的策略是优先判断 Origin,如果请求头中没有包含 Origin 属性,再根据实际情况判断是否使用 Referer 值,从而增加攻击难度。
无法确认来源域名情况
如果 Origin 和 Referer 都不存在,建议直接进行阻止,特别是如果您没有使用随机 CSRF Token(参考下方)作为第二次检查。
如何阻止外域请求
通过Header的验证,我们可以知道发起请求的来源域名,这些来源域名可能是网站本域,或者子域名,或者有授权的第三方域名,又或者来自不可信的未知域名。
我们已经知道了请求域名是否是来自不可信的域名,我们直接阻止掉这些的请求,就能防御CSRF攻击了吗?
且慢!当一个请求是页面请求(比如网站的主页),而来源是搜索引擎的链接(例如百度的搜索结果),也会被当成疑似CSRF攻击。所以在判断的时候需要过滤掉页面请求情况,通常Header符合以下情况:
Accept: text/html
Method: GET
但相应的,页面请求就暴露在了CSRF的攻击范围之中。如果你的网站中,在页面的GET请求中对当前用户做了什么操作的话,防范就失效了。
例如,下面的页面请求:
GET https://example.com/addComment?comment=XXX&dest=orderId
注:这种严格来说并不一定存在CSRF攻击的风险,但仍然有很多网站经常把主文档GET请求挂上参数来实现产品功能,但是这样做对于自身来说是存在安全风险的。
另外,前面说过,CSRF大多数情况下来自第三方域名,但并不能排除本域发起。如果攻击者有权限在本域发布评论(含链接、图片等,统称UGC),那么它可以直接在本域发起攻击,这种情况下同源策略无法达到防护的作用。
综上所述:同源验证是一个相对简单的防范方法,能够防范绝大多数的CSRF攻击。但这并不是万无一失的,对于安全性要求较高,或者有较多用户输入内容的网站,我们就要对关键的接口做额外的防护措施。
3.2 CSRF Token
CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。
CSRF Token 原理
- 将CSRF Token输出到页面中
- 页面提交的请求携带这个Token
- 服务器验证Token是否正确
我们需要知道的是:
- Token的值必须是随机生成的
- 开发人员只需为当前会话生成一次Token。在初始生成此 Token 之后,该值将存储在会话中,并用于每个后续请求,直到会话过期。
- 当最终用户发出请求时,服务器端必须验证请求中 Token 的存在性和有效性,与会话中找到的 Token 相比较。如果在请求中找不到Token,或者提供的值与会话中的值不匹配,则应中止请求,应重置 Token 并将事件记录为正在进行的潜在 CSRF 攻击。
3.3 双重 Cookie 认证
- 流程:
- 步骤1:在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串(例如csrfcookie=v8g9e4ksfhw1213)
- 步骤2:在前端向后端发起请求时,取出Cookie,并添加到URL的参数中( www.a.com/comment?csr…
- 步骤3:后端接口验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝。
- 优点:
- 无需使用Session,适用面更广,易于实施。
- Token储存于客户端中,不会给服务器带来压力。
- 相对于Token,实施成本更低,可以在前后端统一拦截校验,而不需要一个个接口和页面添加。
- 缺点:
- Cookie中增加了额外的字段。
- 如果有其他漏洞(例如XSS),攻击者可以注入Cookie,那么该防御方式失效。
- 难以做到子域名的隔离。
- 为了确保Cookie传输安全,采用这种防御方式的最好确保用整站HTTPS的方式,如果还没切HTTPS的使用这种方式也会有风险。
3.4 Samesite Cookie 属性
Google起草了一份草案来改进HTTP协议,那就是为Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie,Samesite 有两个属性值,Strict 为任何情况下都不可以作为第三方 Cookie ,Lax 为可以作为第三方 Cookie , 但必须是Get请求
参考:
3.5 其他方法
验证码和密码被认为是对抗CSRF攻击最简洁而有效的防御方法。
四、总结
4.1 CSRF攻击特点
- 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
- 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
- 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
- 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪
CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。
-
点击链接查看xxx
-
指定的是本站某个用户关注的请求
4.2 与XSS的区别
本质来说:
- xss是代码注入的问题
- csrf是HTTP问题