CSRF是什么?

CSRF的全称是Cross-Site RequestForgery,中文意思为跨站请求伪造。

服务器端与客户端通过Cookie来标识和认证用户,通常Cookie会存放一个带签名的用户ID,每次请求服务器的时候浏览器就会自动把这个Cookie带上(只要Cookie不过期),服务器根据这个用户的ID就知道当前具体处理的是哪个用户。

只有你给这个网站设置了Cookie,那么请求这个网站的服务器时就会自动带上Cookie,即使你在其他网站中调用这个网站的接口,也会把Cookie带给服务器,不要问我为什么,这个浏览器的机制。

根据这个漏洞,就出现了跨站请求伪造。

CSRF是一个怎样的过程?

为了详细解释CSRF攻击是怎样一个过程,这里以一个留言的例子来说明。假设某个网站有这样一个留言程序,提交留言的接口如下所示:

http://domain_a.com/guestbook

用户通过POST提交 content 字段就能成功留言。服务器端会自动从Session数据中判断是谁提交的数据,补足 ​​username​​​ 和 ​​updatedAt​​ 两个字段后向数据库中写入数据

function (req, res) {
var content = req.body.content || '';
var username = req.session.username;
var feedback = {
username: username,
content: content,
updatedAt: Date.now()
};
db.save(feedback, function (err) {
res.writeHead(200);
res.end('Ok');
});
}

正常的情况下,谁提交的留言,就会在列表中显示谁的信息。如果某个攻击者发现了这里的接口存在CSRF漏洞,那么他就可以在另一个网站(​​http://domain_b.com/attack​​)上构造了一个表单提交,如下所示:

<form id="test" method="POST" action="http://domain_a.com/guestbook">
<input type="hidden" name="content" value="我是钓鱼网站" />
</form>
<script type="text/javascript">
$(function () {
$("#test").submit();
});
</script>

这种情况下,攻击者只要引诱某个 ​​domain_a​​​ 的登录用户访问这个 ​​domain_b​​​ 的网站,就会自动提交一个留言。由于在提交到 ​​domain_a​​​ 的过程中,浏览器会将 ​​domain_a​​​ 的Cookie发送到服务器,尽管这个请求是来自 ​​domain_b​​ 的。这就是钓鱼网站的做法。

以上过程就是一个CSRF攻击的过程。这里的示例仅仅是一个留言的漏洞,如果出现漏洞的是转账的接口,那么其危害程度可想而知。

怎么防止漏洞?

一般在做页面渲染的时候,调取一下获取Token的接口,以后每次请求再发送回去,服务器就会根据这个值去验证是否正确,而钓鱼网站是没有这个Token的,为了防止Token被模仿,这个值通常是随机字符串生成的,Token称为令牌,是客户端的唯一标识。

const generateRandom = function(len) {
return crypto.randomBytes(Math.ceil(len * 3 / 4))
.toString('base64')
.slice(0, len);
}