- 一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
什么是 CSRF?
CSRF 是Cross-Site Request Forgery的首字母缩写词。中文名称:跨站请求伪造。
一般来说,攻击者通过伪造用户的浏览器的请求,向访问一个用户自己曾经认证访问过的网站发送出去,使目标网站接收并误以为是用户的真实操作而去执行命令。常用于盗取账号、转账、发送虚假消息等。攻击者利用网站对请求的验证漏洞而实现这样的攻击行为,网站能够确认请求来源于用户的浏览器,却不能验证请求是否源于用户的真实意愿下的操作行为。
通常防止 CSRF 的方法是发送由每个 HTTP 请求生成的唯一令牌。如果服务器上的令牌与请求中的令牌不匹配,则会向用户显示错误。
标准 CSRF 保护
这时你可以使用令牌防止 CSRF 的一种方法:
const inital_token = '...';
const secure_fetch = (token => {
const CSRF_HEADER = 'X-CSRF-TOKEN';
return (url) => {
const response = await fetch(url, {
method: 'POST',
headers: {
[CSRF_HEADER]: token
}
});
response.then(res => {
token = res.headers[CSRF_HEADER]
});
return response;
};
})(inital_token);
此代码使用fetch API在HTTP标头中发送和接收安全令牌。在后台,你应该在页面加载时生成第一个初始令牌。在服务器上,对于每个AJAX请求,您应该检查令牌是否有效。
如果打开了多个选项卡,以上方法可能会失效。但是这个问题有一个简单的解决方案,那就是跨表通信。
跨表通信解决方案
系统端库
你可以使用Sysend 库,这是专门为此目的创建的开源解决方案。它简化了跨标签通信。
let token;
sysend.on('token', new_token => {
token = new_token;
});
// ...
sysend.broadcast('token', token);
sysend库基本函数使用简单示例
这就是你如何使用这个库来修复 CSRF 保护:
const inital_token = '...';
const secure_fetch = (token => {
const CSRF_HEADER = 'X-CSRF-TOKEN';
const EVENT_NAME = 'csrf';
sysend.on(EVENT_NAME, new_token => {
// get new toke from different tab
token = new_token;
});
return (url) => {
const response = await fetch(url, {
method: 'POST',
headers: {
[CSRF_HEADER]: token
}
});
response.then(res => {
token = res.headers[CSRF_HEADER];
// send new toke to other tabs
sysend.broadcast(EVENT_NAME, token);
});
return response;
};
})(inital_token);
使用 sysend 进行 CSRF 保护的secure_fetch 函数
你所要做的就是在发送请求时从其他选项卡发送和接收一条消息。这样。将让用户在想要打开许多选项卡时使用具有 CSRF 保护的功能。
广播频道
这是使用广播频道的最简单示例:
const channel = new BroadcastChannel('my-connection');
channel.addEventListener('message', (e) => {
console.log(e.data); // 'some message'
});
channel.postMessage('some message');
广播频道的基本用法
因此,使用这个简单的 API,您可以执行我们之前所做的相同操作:
const inital_token = '...';
const secure_fetch = (token => {
const CSRF_HEADER = 'X-CSRF-TOKEN';
const channel = new BroadcastChannel('csrf-protection');
channel.addEventListener('message', (e) => {
// get new toke from different tab
token = e.data;
});
return (url) => {
const response = await fetch(url, {
method: 'POST',
headers: {
[CSRF_HEADER]: token
}
});
response.then(res => {
token = res.headers[CSRF_HEADER];
// send new token to other tabs
channel.postMessage(token);
});
return response;
};
})(inital_token);
使用 BroadcastChannel 的带有 CSRF 保护的secure_fetch 函数
结论
以上就是保护你的网站免受攻击的方法。