概念
CSRF,Cross Site Request Forgery,跨站请求伪造。
为什么跨站的请求需要伪造?
因为浏览器实现了同源策略,这里可以将站和源视为同一个概念。
同源策略
The same-origin policy restricts how a document or script loaded from one origin can interact with a resource from another origin. It is a critical security mechanism for isolating potentially malicious documents.。 ——MDN
源是什么?
JavaScript可以通过document.origin
查看当前文档所属的源,但是不能改变源;源由协议(https),域名(segmentfault)和端口(默认为80)构成;所谓同源,要求协议,域名和端口都相同。
从一个源加载的文档或脚本与另一个源的资源进行交互,可以理解为,从当前源(网站)加载的文档或脚本向另一源(网站)发送请求(请求的URL中包含了另一源)。浏览器对这种行为进行了限制。
如何限制,实验环节
分别在segmentfault和baidu发送xhr请求,请求一篇segmentfault的文档,其url为https://segmentfault.com/a/11...,相关截图如下
在segmentfault中发送请求后Console,Network中Headers和Response显示如下
这里我们要关注一下几点,控制台没有报错,请求时包含cookie,响应成功,且返回的文档可见。
在baidu中发送请求后Console,Network中Headers和Response显示如下
首先控制台里报错了,No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://www.baidu.com' is therefore not allowed access.;其次请求时没有包含cookie,响应虽然成功,但返回的内容不可见。
小结一下
通过Response Headers,我们可以判断请求已经发给segmentfault,并被处理,segmentfault也给了我们请求的文档。那么所谓的限制呢?其一,在源为百度的文档中,通过JavaScript发送给segmentfault的请求没有带上segmentfault的cookie;其二,segmentfault其实有返回的内容,但是浏览器不让我们看,not allowed access。
略加思索
The same-origin policy restricts how a document or script loaded from one origin can interact with a resource from another origin. It is a critical security mechanism for isolating potentially malicious documents.。 ——MDN
这里用了restricts,也就是还有允许的。比如说,在一个网站中可以引用其他网站的图片,脚本等,它们都是有src属性的标签。
如果在baidu中通过img的src标签发送请求 https://segmentfault.com/a/11... 会如何?
与之前发送xhr相比,通过img标签发送请求,这会把cookie给带上了!!!还发出去了!!!虽然返回的结果依然不可见。
所以危害在哪里?
引用CSRF 攻击的应对之道的例子
CSRF 攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在并未授权的情况下执行在权限保护之下的操作。比如说,受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?... 可以使 Bob 把 1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?...。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src=”http://bank.example/withdraw?... ”,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外。
引用[浅谈CSRF攻击方式](https://www.cnblogs.com/hyddd...
防御之道
并不是所有的请求都要进行CSRF防御,很多读取数据的请求不用进行CSRF防御,因为返回的结果本来就不可见;那些操作数据的请求往往需要进行CSRF防御,例如修改账户信息之类的。
CSRF只是发送请求时带上了相应的cookie,但这个cookie对攻击者来说还是不可见,不可操作的,同时攻击者所拥有的也只有这个他自己不可见,不可操作的cookie。所以防御的基本思想就是在请求中添加额外的验证信息(token),不仅仅通过cookie来验证请求,方法大体有以下几种:
服务端验证HTTP Referrer字段
对于每个请求,验证其Referrer,是否同源
在请求地址中添加token并在服务端验证
例如在segmentfault中给文章点赞,就会发送这样的请求
我揣测segmentfault的防御机制是这样的,每当用户请求页面时,页面里会包含一段随机数(token),在发送关健请求时,token会作为请求的参数被带上。CSRF的攻击者无法得到token,而服务端凭借此token判断请求正常与否。此外,即使同样的用户发出同样的请求,得到页面里的token还是会不一样。亲测实验证明,在一定时间范围内,token不会变化,可以说token是有有效期的。
在请求头中添加自定义token
例如在知乎中给文章点赞
结合wiki中对CSRF的Prevention的介绍,知乎的这种方式被称为Cookie-to-header token。其主要过程如下,
- 服务端发送cookie,其中含有xsrf=fajflafjaajf21ejlkja,其值为随机数(token)
- 客户端读取cookie,并将token读取出来
- 客户端在发送需要csrf防御的请求时,例如知乎例子中的点赞,就会将token设为请求头的值,例如知乎例子中X-Xsrftoken,添加至请求中(csrf,xsrf是一个玩意儿)
- 服务端会根据token与cookie中值是否相符,判断请求的合法性
要实现这样的过程,就要求cookie中xsrf字段没有设置httpOnly
其次这样做能防御csrf,是因为其他源中的JavaScript无法读取知乎源的cookie,只有知乎自己源下的JavaScript可以读取自己的cookie。对应之前的说明图中,即使用户在没有登出A网站的情况下,访问B网站,B网站带着A网站的cookie向A网站发出请求,但是B网站无法读取A网站的cookie,发出的请求中无法带有相应的请求头,或请求头中的值无法与cookie匹配,例如知乎中的X-Xsrftoken。A网站收到这样的请求,就能辨别出它是恶意的请求了。
参考资料
- Same-origin policy
- CSRF 攻击的应对之道
- 浅谈CSRF攻击方式
- CSRF Introduction and what is the Same-Origin Policy? - web 0x04
- Wiki中CSRF
推荐文档
- Cross-Site Request Forgery (CSRF))
- Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet_Prevention_Cheat_Sheet#Viewstate_.28ASP.NET.29)
本文转载于:CSRF浅析