CSRF攻击原理及预防
CSRF 简介
Cross Site Request Forgy
跨站请求伪造
CSRF是一种夹持用户在已经登陆的web应用程序上执行非本意的操作的攻击方式。
相比于XSS,CSRF是利用了系统对页面浏览器的信任,XSS则利用了系统对用户的信任。
CSRF 攻击原理
CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。
构成CSRF攻击是有条件的:
- 客户端必须一个网站并生成cookie凭证存储在浏览器中
- 该cookie没有清除,客户端又tab一个页面进行访问别的网站
CSRF 危害
✸ 冒充用户发送邮件,发消息,发帖
✸ 盗取用户的账号,消费(购买商品,虚拟货币)、转账
✸ 损坏网站名誉
CSRF造成的问题包括:个人隐私泄露以及财产安全。
CSRF 防御措施
① 使用验证码
② 添加token
③ 验证Referer
④ 禁止第三方网站带Cookies
⑤ 在http头中自定义属性并验证
⑥ 尽量使用POST,限制GET
验证 HTTP Referer 字段
禁止来自第三方网站的请求
http头中有一个referer,记录http请求的来源地址,访问一个安全受限的页面的请求必须来自同一个网站。当用户要提交一个请求时,请求的referer值需是提交按钮(触发请求)所在的页面的URL。
禁止第三方网站带Cookies
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
user.js
// 登录成功,设置cookie
ctx.cookies.set('userId', user.id,{
httpOnly:false,
sameSite: 'strict'
});
但是问题是sameSite这个属性只有chrome和Opera支持
使用验证码-Verification Code
每次用户提交都在表单上填写一个图片上的随机字符串作为验证码。
在请求地址中添加token并验证
CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信息不存在于Cookie之中。
在请求中放入攻击者不能伪造的信息,并且该信息不在cookie中,开发者可以在http请求中以参数的形式加入一个随机产生的token,并且在服务器建立一个拦截器来验证这个token。如果请求中没有token或者token中内容不正确的话,表示可能是攻击者发动的攻击。
例子:
-
用户访问某个表单页面。
-
服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。
-
在页面表单附带上Token参数。
-
用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。
这个Token的值必须是随机的,不可预测的。由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。
在http头中自定义属性并验证
通过XHR这个类,一次性给所有该类请求加上csrftoken这个http头属性,并且把token值放入其中。
尽量使用POST,限制GET
GET接口太容易被拿来做CSRF攻击,看第一个示例就知道,只要构造一个img标签,而img标签又是不能过滤的数据。接口最好限制为POST使用,GET则无效,降低攻击风险。
当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。
用GET方式攻击更简单
如果不用用户点击
csrf.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>csrf demo</title>
</head>
<body>
hello,这里什么也没有。
<img src="localhost:1521/ajax/addComment?postid=13&content=我是来自get的评论" />
<script>
</script>
</body>
</html>
这里用户需要点击
csrf.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>csrf demo</title>
</head>
<body>
hello,这里什么也没有。
<a href="localhost:1521/ajax/addComment?postid=13&content=我是来自get的评论">点击这里有钱啦</a>
<script>
</script>
</body>
</html>
用POST方式攻击
创建iframe, 在iframe中提交和跳转,由于iframe隐藏,所以看不到跳转页。
用的是POST,需要第三方构建表单提交才可以
csrf.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>csrf demo</title>
</head>
<body>
hello,这里什么也没有。
<script>
document.write(
`
<form name="commentForm" target="csrf" method="post" action="http://localhost:1521/post/addComment">
<input name="postId" type="hidden" value="1">
<textarea name="content">来自CSRF!</textarea>
</form>`
);
var iframe = document.createElement('iframe');
iframe.name = 'csrf';
iframe.style.display = 'none';
document.body.appendChild(iframe);
setTimeout(function () {
document.querySelector('[name=commentForm]').submit();
}, 1000);
</script>
</body>
</html>
PHP-CSRF
cookie samesite
HTTP referer头
token