防御 CSRF 的常见方法(如 Token 验证)
防御 CSRF 的常见方法(如 Token 验证)
CSRF(Cross-Site Request Forgery)是一种常见的网络攻击方式,攻击者通过伪造用户身份执行非预期的操作。防御 CSRF 的方法多种多样,其中 Token 验证是最常用的手段之一。除了 Token 验证,还可以通过 SameSite Cookie、双重 Cookie 验证等方式增强安全性。
Token 验证的原理
Token 验证的核心思想是在请求中携带一个随机生成的令牌(Token),服务器通过验证该令牌的合法性来判断请求是否可信。Token 通常存储在用户的会话(Session)或 Cookie 中,并在表单提交或 AJAX 请求时发送给服务器。
Token 的生成和验证流程如下:
- 用户访问页面时,服务器生成一个随机 Token 并存储在 Session 中。
- 服务器将 Token 嵌入到页面表单或返回给前端。
- 用户提交表单或发送请求时,Token 随请求一起发送到服务器。
- 服务器验证 Token 是否与 Session 中存储的一致。
// 示例:生成 CSRF Token(Node.js + Express)
const express = require('express');
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.get('/form', (req, res) => {
res.send(`
<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="${req.csrfToken()}">
<button type="submit">Submit</button>
</form>
`);
});
app.post('/process', (req, res) => {
// 中间件会自动验证 CSRF Token
res.send('Form processed successfully');
});
Token 的存储与传递方式
Token 可以通过多种方式传递给前端,常见的方式包括:
-
表单隐藏字段:将 Token 作为隐藏字段嵌入表单中。
<form action="/submit" method="POST"> <input type="hidden" name="_csrf" value="随机生成的Token"> <!-- 其他表单字段 --> </form>
-
HTTP 请求头:在 AJAX 请求中通过请求头传递 Token。
fetch('/api/data', { method: 'POST', headers: { 'X-CSRF-Token': '随机生成的Token' } });
-
Cookie + 请求头:将 Token 存储在 Cookie 中,前端读取后通过请求头发送。
// 从 Cookie 中读取 Token const token = document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$|^.*$/, '$1'); fetch('/api/data', { method: 'POST', headers: { 'X-XSRF-Token': token } });
SameSite Cookie 属性
SameSite 是 Cookie 的一个属性,用于限制 Cookie 的跨站发送行为。它可以有效防止 CSRF 攻击,尤其是在现代浏览器中广泛支持的情况下。
SameSite 有三个可选值:
Strict
:Cookie 仅在同站请求中发送。Lax
:Cookie 在跨站请求中部分允许发送(如导航跳转)。None
:Cookie 在所有请求中发送(需配合Secure
属性)。
// 设置 SameSite Cookie(Node.js)
res.cookie('sessionID', '12345', {
httpOnly: true,
secure: true,
sameSite: 'Strict'
});
双重 Cookie 验证
双重 Cookie 验证是另一种防御 CSRF 的方法。其原理是将 Token 同时存储在 Cookie 和请求参数中,服务器验证两者是否一致。
实现步骤:
- 用户访问页面时,服务器在 Cookie 中设置一个随机 Token。
- 前端从 Cookie 中读取 Token,并在请求时将其作为参数或请求头发送。
- 服务器比对 Cookie 中的 Token 和请求中的 Token。
// 示例:双重 Cookie 验证
app.get('/api/data', (req, res) => {
const token = req.cookies['CSRF-TOKEN'];
const requestToken = req.headers['x-csrf-token'];
if (token && requestToken && token === requestToken) {
res.send('Valid request');
} else {
res.status(403).send('Invalid CSRF Token');
}
});
其他防御措施
除了上述方法,还可以结合以下措施增强安全性:
-
验证 Referer 和 Origin 头:检查请求的来源是否合法。
app.use((req, res, next) => { const referer = req.get('Referer'); if (referer && !referer.startsWith('https://yourdomain.com')) { return res.status(403).send('Invalid request source'); } next(); });
-
限制敏感操作的 HTTP 方法:确保敏感操作(如修改数据)仅允许 POST、PUT、DELETE 等方法。
app.post('/change-password', (req, res) => { // 处理密码修改逻辑 });
-
短期有效的 Token:为 Token 设置较短的过期时间,减少被利用的风险。
app.use(csrf({ cookie: { maxAge: 3600 } })); // Token 1 小时后过期
实际应用中的注意事项
- 避免 Token 泄露:Token 不应出现在 URL 或日志中,防止被截获。
- 前后端分离架构的适配:在 SPA(单页应用)中,可以通过接口获取 Token 并存储在内存中。
// 前端获取 CSRF Token fetch('/csrf-token', { credentials: 'include' }) .then(res => res.json()) .then(data => { window.csrfToken = data.token; });
- 兼容旧版浏览器:如果使用 SameSite Cookie,需考虑旧版浏览器的兼容性问题。
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:CSRF 攻击的危害