CSRF攻击与防御

一、概念:

        CSRF:也叫XSRF(英文Cross-site request forgery),中文名是跨域请求伪造,也被称为One Click Attack或者Session Riding。CSRF是指恶意用户伪造并诱使用户A在不经意间点击这个链接,而在请求这个伪造的URL时,实际上是请求了某个需要用户A身份认证登录的服务。由于大部分登录服务都是通过Cookie来识别用户的,而这些Cookie存储在用户A登录过服务的浏览器上,只要不关闭浏览器不退出登录,以后访问这个服务就会带上这个Cookie直接登录 。

        此漏洞就是利用保存在浏览器上的Cookie身份信息,不管以哪种方式请求对应的服务,都会自动读取Cookie免登录。尤其是在跨域情况下,虽然由于“跨域”无法用代码中的变量接收返回的内容(不管是Form表单的submit提交还是Ajax方式的提交),但如果不关心返回结果(不判断接口的请求结果是否成功),只是请求而已,此时就有可能在用户不知情的情况下修改了用户A对应的业务数据。

二、攻击条件:

        1、需要用户登录,且用户身份认证信息存在浏览器的Cookie等可重复使用的地方;

        2、Get查询接口URL一般不考虑,只考虑那些Post修改接口;

        3、用户直接点击某链接就可以触发请求另一个接口URL;

三、攻击示例:

        如果用户POST请求输入$userName和$passWord登录银行的某个有CSRF漏洞网站A:http://localhost:8000/bank.php,代码如下:

<?php
const USERNAME = 'abcd';
const PASSWORD = '123456';

//是否登录
$isLogin = false;

//模拟登录
$auth = $_COOKIE['auth'];
if ($auth) {
    //模拟解密过程
    $userInfo = explode(',', $auth);
    if ($userInfo && count($userInfo) === 2) {
        $userName = $userInfo[0];
        $passWord = $userInfo[1];
    } else {
        die('auth失效,请重新登录!');
    }
} else {
    if ($_POST['userName'] && $_POST['passWord']) {
        $userName = $_POST['userName'];
        $passWord = $_POST['passWord'];
    } else {
        die('请输入用户名和密码登录!');
    }
}

if ($userName === USERNAME && $passWord === PASSWORD) {
    //模拟加密过程
    $auth = $userName . ',' . $passWord;
    //cookie24小时后过期
    $expire = time() + 24 * 60 * 60;
    setcookie('auth', $auth, $expire);
    echo '登录成功!';
} else {
    die('登录失败,请输入用户名和密码登录!');
}
// 用户登录成功后,进行密码修改、转帐等一系列操作
$newPwd = $_POST['newPwd'];

        当用户$userName输入:abcd,$passWord输入:123456登录成功时,会在浏览器中设置Cookie:auth=abcd%2C123456。然后用户进行密码修改等操作。

        当用户在未关闭浏览器且未退出帐号时,又点击了一个不可信任、不安全的网站B(与网站A不同的协议、HOST及端口): http://localhost:8001/danger.html,代码如下:

<html>
<head>
    <meta charset="utf-8">
    <title>Dangerous Page</title>
</head>
<body>
<!-- 不关心是否请求成功,只是请求,所以不存在跨域问题 -->
<form action="http://localhost:8000/bank.php" method="POST">
    <input type="hidden" name="newPwd" value="任意密码,随便输" />
    <input type="submit" value="提交" />
</form>
</body>
</html>

        可以看到即使不用输密码,也可以修改密码。虽然是用户本人点击的链接,但并不是用户主观希望修改密码。

        另外,对于一些特殊的、没有Post请求参数,只需用户认证登录就可修改数据的接口(比如用户清除所有未读消息,只需要用户身份信息修改所有未读消息的状态),粘在“同域”下的页面诱使用户点击,虽然此时并未“跨域”,但用户并不清楚这个URL的实际作用,也会在不经意间修改了自己的数据。

四、防御措施:

        1、服务端使用Referer或Origin请求头判断请求来源,因为前端在发起请求时无法修改设置这两个请求头,只能是浏览器自动设置,参见:禁止修改的消息首部 - 术语表 | MDN

        说明:postman等模拟请求工具虽然可以修改Header头,但它只是模拟成了一个浏览器发起请求,实际浏览器并不能修改上述请求头。

        2、保证某些请求参数必输,且这些参数不放在URL中,且这些参数不存储在浏览器可以重复读取利用的地方(比如Cookie中)。在这种情况下,必输参数只能放在Post参数中且该参数无法被恶意用户通过抓包等方式获取到,并且无法找到规律,请求时就会报缺少参数或参数错误无法访问服务。最常用的是使用身份令牌token:

        这个token是与用户身份相关的随机数,且有有效期。每次请求都需要通过POST表单提交这个token,请求到服务端之后服务端要校验这个token值对不对。由于这个token没保存在Cookie中,只能通过Post参数传递,即使带上了Cookie中正确的身份信息,token缺失或者不正确都会直接被服务拒绝。这个token只有用户本身登录成功后才能拿到,别人无法伪造。

        当然,如果用户自己从正确的请求页面中抓包拿到token,在一个新页面发请求,这种情况不考虑在内,毕竟这属于和用户密码泄露一样,并不能通过系统避免。

        3、对于无Post必输参数的请求,单纯地增加Header头的Referer或Origin请求头校验,在“同域”下仍然不能避免诱使用户无感知点击链接,只能通过增加token的方式来解决。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值