WEB安全之-CSRF(跨站请求伪造)
WEB安全中经常谈到的两个东西:XSS和CSRF。
这两个概念在前端面试中也经常被问到,只要涉及到WEB安全的东西就必须提到他们哥俩,从我以往的面试经历中我多次死在这两个东西上,知道这两个东西但让我仔细描述下就瞎几把乱扯,结果可想而知。
这几天也查阅了相关书籍,终于把这两个东西搞明白了,为此决定写篇文章来记录他们俩以备今后复习,这篇先介绍CSRF。
CSRF是什么鬼
CSRF(Cross Site Request Forgery) 跨站请求伪造。也被称为One Click Attack和Session Riding,通常缩写为CSRF或XSRF。如果从名字你还不不知道它表示什么,你可以这样理解:攻击者(黑客,钓鱼网站)盗用了你的身份,以你的名义发送恶意请求,这些请求包括发送邮件、发送消息、盗取账号、购买商品、银行转账,从而使你的个人隐私泄露和财产损失。
CSRF漏洞现状
CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到2006年才开始被关注,2008年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:纽约时报,Metafilter,YouTube和百度。。。。而现在,互联网的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。
CSRF攻击实例
听了这么多,可能大家还云里雾里,光听概念可能大家对于CSRF还是不够了解,下面我将举一个例子来让大家对CSRF有一个更深层次的理解。
我们先假设支付宝存在CSRF漏洞,我的支付宝账号是lyq,攻击者的支付宝账号是xxx。
然后我们通过网页请求的方式 http://zhifubao.com/withdraw?account=lyq&amount=10000&for=lyq2 可以把我账号lyq的10000元转到我的另外一个账号lyq2上去。通常情况下,该请求发送到支付宝服务器后,服务器会先验证该请求是否来自一个合法的session并且该session的用户已经成功登陆。攻击者在支付吧也有账号xxx,他知道上文中的URL可以进行转账操作,于是他自己可以发送一个请求 http://zhifubao.com/withdraw?account=lyq&amount=10000&for=xxx 到支付宝后台。
但是这个请求是来自攻击者而不是来自我lyq,所以不能通过安全认证,因此该请求作废。这时,攻击者xxx想到了用CSRF的方式,他自己做了个黄色网站,在网站中放了如下代码:http://zhifubao.com/withdraw?account=lyq&amount=10000&for=xxx 并且通过黄色链接诱使我来访问他的网站。
当我禁不住诱惑时就会点了进去,上述请求就会从我自己的浏览器发送到支付宝,而且这个请求会附带我的浏览器中的cookie。大多数情况下,该请求会失败,因为支付宝要求我的认证信息,但是我如果刚访问支付宝不久,还没有关闭支付宝页面,我的浏览器中的cookie存有我的认证信息,这个请求就会得到响应
,从我的账户中转10000元到xxx账户里,而我丝毫不知情,攻击者拿到钱后逍遥法外。所以以后一定要克制住自己,不要随便打开别人的链接。
CSRF原理
下面一张图简单阐述了CSRF的原理
从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成以下两个步骤:
- 登录受信任网站A,并在本地生成Cookie。
- 在不登出A的情况下,访问危险网站B。
看到这里,你也许会问:“如果我不满足以上两个条件中的一个,我就不会受到CSRF攻击”。是滴,确实如此,但是你不能保证以下情况不会发生:
- 你不能保证你登录了一个网站之后,不再打开一个tab页面并访问其它的网站(黄网)。
- 你不能保证你关闭浏览器之后,你本地的Cookie立刻过期,你上次的会话已经结束。
上述中所谓的攻击网站,可能就是一个钓鱼网站或者黄色网站。
几种常见的攻击类型
GET类型的CSRF
这种类型的CSRF一般是由于程序员安全意识不强造成的。GET类型的CSRF利用非常简单,只需要一个HTTP请求,所以,一般会这样利用:
<img src=http://wooyun.org/csrf?xx=11 />
在访问含有这个img的页面后,成功向http://wooyun.org/csrf?xx=11 发出了一次HTTP请求。
所以,如果将该网址替换为存在GET型CSRF的地址,就能完成攻击了。
POST类型的CSRF
这种类型的CSRF危害没有GET型的大,利用起来通常使用的是一个自动提交的表单,如:
<form action=http://wooyun.org/csrf.php method=POST>
<input type="text" name="xx" value="11" />
</form>
<script> document.forms[0].submit(); </script>
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。
CSRF如何防御
验证HTTP Referer字段
根据HTTP协议,在HTTP头部中有一个Referer字段,它记录了该HTTP请求所在的地址,表示HTTP请求从那个页面发出的。比如当你访问 http://zhifubao.com/withdraw?account=lyq&amount=10000&for=xxx ,用户必须先登录支付宝网站,然后通过点击页面的的按钮来触发转账事件。此时,转账请求的Referer值就是转账页面所在的URL,通常是以zhifubao.com域名开头的地址。如果攻击者要实行CSRF攻击,那么他只能在自己的站点构造请求,此时Referer的值就指向黑客自己的网站。因此要防御CSRF攻击,支付宝只需要对每一个转账请求验证其Referer值,如果是以zhifubao.com开头的域名,则是合法请求,相反,则是非法请求并拒绝。
这种方法的好处就是简单易行,只需要在后台添加一个拦截器来检查Referer即可。然而这种办法并不是万无一失,Referer的值是由浏览器提供的,一些低级的浏览器可以通过某种方式篡改Referer的值,这就给了攻击者可乘之机;而一些高级浏览器处于安全考虑,可以让用户设置发送HTTP请求时不再提供Referer值,这样当他们正常访问支付宝网站时,因为没有提供Referer值而被误认为CERF攻击,拒绝访问。
充分利用好cookie的SameSite属性
SameSite选项通常由Strict、Lax和None三个值
- Strict最为严格,如果cookie设置了Strict,那么浏览器会完全禁止第三方Cookie。
- Lax相对宽松一点,在跨站点的情况下,从第三方站点的链接打开和从第三方站点提交Get的表单都会携带cookie.但是如果在第三方站点中使用Post方法或者通过img、iframe等标签加载的URL,都不会携带Cookie。
- None, 任何情况下都会发送Cookie。
但是现在大部分的网站静态资源都放在单独的域名下,所以通过设置Cookie的SameSite为Strict、Lax是不能正常运行的,所以这个方法只适用静态资源跟服务器接口在同一个站点下的网站。
在请求地址中添加 token 并验证
CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。而对于 POST 请求来说,要在 form 的最后加上 <input type=”hidden” name=”csrftoken” value=”tokenvalue”/>
,这样就把 token 以参数的形式加入请求了。但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token。
该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个 token,并马上就可以发动 CSRF 攻击。为了避免这一点,系统可以在添加 token 的时候增加一个判断,如果这个链接是链到自己本站的,就在后面添加 token,如果是通向外网则不加。不过,即使这个 csrftoken 不以参数的形式附加在请求之中,黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击。这也是一些用户喜欢手动关闭浏览器 Referer 功能的原因。
验证码
验证码,强制用户必须与应用进行交互,才能完成最终请求。通常情况下,验证码能够很好的遏制CSRF攻击。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段。