CSRF利用与防御

背景

跨站请求伪造(Cross-site request forgery),也称作XSRF,是利用用户在当前已登录的Web应用程序上执行非本意操作的攻击方法。

利用

GET类型CSRF

add_cart.php

$goods_id = $_GET['goods_id'];
add_cart($goods_id);  

此处就存在一个购物车的GET SSRF漏洞,可以通过构造下面一个连接

http://tb.com/add_cart.php?goods_id=1  

并可以将这个链接发给任何一个人,这个人的淘宝购物车就会被添加一个你制定的商品。

POST类型CSRF

那如果goods_id是通过$_POST取的是不是就没问题了? 但还是能构造一个POST表单来骗取你点击,危害不那么直接,但还是存在危害。

可以发现$_GET取参导致的CSRF更加容易被利用,所以我们应当分清GET/POST/REQUEST的区别,在对应的场景使用正确的方法,不要乱用。

修复方案

目标: 我们需要验证每一次的请求都是对应的用户发过来的,就能防止CSRF。

实现:

  • 1.正确的使用GET/POST/REQUEST(减轻乱用GET/REQUEST的影响)
  • 2.验证所有请求Referer来源(杜绝站外构造链接造成的CSRF)
  • 3.后端生成Token并传给前端,前端提交请求时带上这个Token由后端验证Token。(杜绝站内构造的链接造成的CSRF)

第二点只需要按照具体的业务限制Referer是的来源是自己可控的站点就行。

第三点的实现分为两种业务类型:

1.后端渲染业务(使用了模板引擎,比如Smarty/Jinja2等)

后端生成一个随机数存入SESSION。

# 生成一个随机数
$token = random_bytes(64);

# 将随机数存入SESSION
$_SESSION['csrf_token'] = $token;

通过下面两种方式渲染在页面上(meta和hidden input)

<html>  
<head>  
<meta name="csrf-token" content="{{ $csrf_token }}">  
</head>  
<body>  
<input type="hidden" name="csrf-token" value="{{ $csrf-token }}">  
</body>  
</html>  

发起请求时带上Token给后端进行验证

// 将所有的Ajax Request Header都带上Token
$.ajaxSetup({
        beforeSend: function(xhr, settings) {
            xhr.setRequestHeader("X-CSRFToken", $("input[name=csrf-token]").val())
        }
    })

// 后面业务正常使用即可
$.get('add_cart.php', {goods_id: 1}, function(result){
    // do something
},'JSON')

2.前后端分离业务(前端使用Angular等全部通过API交互的)

每次登陆时生成的一个CSRF的SESSION,前端通过获取该SESSION对应的Cookie在每次请求时带上,后端进行验证。

注意事项

  • : token的生成一定要快,因为每次请求都会使用到;不一定要非常高的算法,因为只是用来验证来源的,不能被伪造即可。
  • 跟用户关联: 每个Token生成只能和一个用户关联,确保不被批量利用。
  • 不断变化: 每个页面的Token都应该不一样。
  • 只能用一次: 一个Token被验证后应该及时清除并生成新的Token。