简介
CSRF(Cross Site Request Forgery),跨站域请求伪造,也被称为 XSRF。简单来说,攻击者通过伪装成来自受信任用户的请求来利用受信任的网站。比如通过伪造受信任的网站的 Cookie 来模拟 HTTP 请求,如下图:
在步骤5中,浏览器向A发送了请求,但这个请求并非是由用户主观发起的,而是被网站B诱导产生的,而这个请求中携带了用户的Cookie,所以网站A无法辨别出该请求是个攻击请求。
一、攻击类型
1、Get类型
假设网站 A 修改密码的方式为 Get 请求,在请求时通过携带 Cookie 来验证用户信息,请求地址为:a.com/changePassword?newPassword=123456 ,攻击者获取 A 网站修改密码的链接和参数后,将其伪装
在网站 B 中,并诱导用户点击伪装链接:a.com/changePassword?newPassword=654321 ,若用户不小心点击了这个链接,那么用户的密码就会被改为 654321 。
2、POST类型
还是以修改密码为例,此时 A 网站将请求方式改为了 POST 请求,B 网站在 index 页面中含有以下代码:
<formaction="https://a.com/changePassword"method="post"><inputtype="hidden"name="newPassword"value="654321"><buttontype="submit">提交</button></form><scripttype="text/javascript">$(function(){ $("button")[0].click(); }); </script>
上述代码当用户访问 B 网站的 index 页面时,就会自动提交更改密码的请求,从而将用户的密码改为 654321 。如果换成扣费请求,并且 A 网站存在这样的漏洞,那么当用户访问这种攻击性网站时就会产生不必要的损失。
二、防御方法
1、验证HTTP请求中的Referer
如上图,无论是 GET 还是 POST 请求,或者其他类型的请求,在 HTTP 的请求标头中我们可以通过获取 HTTP 请求头中的 Referer 字段来进行黑白名单判断。通过匹配来源的域名,如果不在白名单内的,那么网站可以拒绝此类请求。
此处以PHP代码为例:
function checkRefer(){ $referer= isset($_SERVER["HTTP_REFERER"])?$_SERVER["HTTP_REFERER"]:""; $matches=array(); if(preg_match("@^(?:http://|https://)?([^/]+)@i", $referer,$matches)) { $host=$matches[1]; preg_match('/[^.]+\.[^.]+$/', $host, $matches); $domain=isset($matches[0])?$matches[0]:''; $whiteDomain = ["a.com","b.com"]; if($domain == ''){ return false; } foreach ($whiteDomain as $curDomain) { if(strpos($domain,$curDomain)!==false){ return true; } } return false; } else { return false; } }
这种方法的弊端是如果攻击者通过伪造 Referer 来发起请求,那么就无法判断其真实性了。
2、添加验证码
对于一些重要业务的请求,可增设图形验证码、短信验证码措施,以防止用户账号被盗用产生不必要的损失。但这种措施,如果对于全站来实施不太可行,会影响用户的体验感。
3、增加 CSRF Token 验证
根据上述的介绍可知 CSRF 攻击主要借助于存储用户验证信息的 Cookie,攻击者通过伪造用户请求,在用户不知道的情况下利用用户的 Cookie 来通过安全验证。因此要想抵御 CSRF 攻击,我们可以在请求中增加一些攻击者不可伪造的信息,并且这个信息不可存储在 Cookie 中。比如可以参数的形式传递一个随机生成的 token 串,当服务器接收到请求后,会验证这个 token,如果请求中缺失或者经过加密解密后这个token不正确,那么可判定为 CSRF 攻击并拒绝该请求。
以 PHP 的 Laravel 框架为例,已经加入了 CSRF Token 的处理,开发者只需要进行简单的配置就可以开启这个功能。
但这个方法的缺点就是难以保证 token 信息的安全,比如攻击者可以通过伪造 Referer 等手段,获取源站的 token 值,从而发起 CSRF攻击。
无论选择哪种方式进行防御,开发者在码代码的同时,除了完成需求功能时,还应该加入安全性相关因素作为考虑点。