如何使你的网站免受CSRF 攻击?

209 阅读2分钟
  1. 一起养成写作习惯!这是我参与「掘金日新计划 · 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 函数

结论

以上就是保护你的网站免受攻击的方法。