跨站请求伪造(CSRF)防御
CSRF攻击利用用户已认证身份,诱导用户发起恶意请求,是Web安全的重要防线。
攻击原理
CSRF(Cross-Site Request Forgery)攻击流程:
- 用户登录受信任网站A,获取认证Cookie
- 用户访问恶意网站B
- 网站B向网站A发送伪造请求
- 浏览器自动携带网站A的Cookie
- 网站A误认为是用户本人操作
典型攻击场景
HTML
<!-- 恶意网站中的隐藏表单 -->
<form action="https://bank.com/transfer" method="POST" id="stealForm">
<input type="hidden" name="to" value="attacker" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>document.getElementById('stealForm').submit();</script>
防御策略
1. CSRF Token
最有效的防御方案,核心原理:服务器生成随机Token,前端请求必须携带。
JavaScript
// 服务端生成Token(Node.js示例)
const crypto = require('crypto');
function generateCSRFToken() {
return crypto.randomBytes(32).toString('hex');
}
// 存储Token并返回
app.get('/csrf-token', (req, res) => {
const token = generateCSRFToken();
req.session.csrfToken = token;
res.json({ csrfToken: token });
});
JavaScript
// 前端请求携带Token
async function fetchWithCSRF(url, options = {}) {
const { csrfToken } = await fetch('/csrf-token').then(r => r.json());
return fetch(url, {
...options,
headers: {
...options.headers,
'X-CSRF-Token': csrfToken
}
});
}
JavaScript
// 服务端验证Token中间件
function csrfProtection(req, res, next) {
const token = req.headers['x-csrf-token'];
const sessionToken = req.session.csrfToken;
if (!token || token !== sessionToken) {
return res.status(403).json({ error: 'CSRF token验证失败' });
}
next();
}
app.post('/api/sensitive-action', csrfProtection, (req, res) => {
// 处理敏感操作
});
2. SameSite Cookie属性
Cookie的SameSite属性限制跨站请求携带Cookie。
JavaScript
// 设置Cookie时指定SameSite
res.cookie('sessionId', sessionId, {
httpOnly: true,
secure: true,
sameSite: 'strict' // 或 'lax'
});
| SameSite值 | 行为 | 适用场景 |
|---|---|---|
strict | 完全禁止跨站发送 | 高安全要求 |
lax | 允许GET链接跳转携带 | 平衡安全与体验 |
none | 允许跨站发送(需配合secure) | 第三方集成 |
3. Referer检查
验证请求来源是否合法。
JavaScript
function checkReferer(req, res, next) {
const referer = req.headers.referer || req.headers.referrer;
if (!referer) {
// 无Referer时的策略
return res.status(403).json({ error: '缺少Referer' });
}
const allowedOrigins = ['https://myapp.com', 'https://www.myapp.com'];
const refererOrigin = new URL(referer).origin;
if (!allowedOrigins.includes(refererOrigin)) {
return res.status(403).json({ error: '非法请求来源' });
}
next();
}
4. 双重Cookie验证
前端从Cookie读取Token并在请求中发送,服务端对比两者。
JavaScript
// 前端:从Cookie读取并放入请求头
function getCsrfFromCookie() {
const match = document.cookie.match(/csrfToken=([^;]+)/);
return match ? match[1] : null;
}
fetch('/api/action', {
method: 'POST',
headers: {
'X-CSRF-Token': getCsrfFromCookie()
}
});
JavaScript
// 服务端:对比Cookie与请求头中的Token
function doubleCookieCSRF(req, res, next) {
const cookieToken = req.cookies.csrfToken;
const headerToken = req.headers['x-csrf-token'];
if (!cookieToken || !headerToken || cookieToken !== headerToken) {
return res.status(403).json({ error: 'CSRF验证失败' });
}
next();
}
前端框架集成
React示例
jsx
// CSRF Token Provider
import { createContext, useContext, useEffect, useState } from 'react';
const CSRFContext = createContext();
export function CSRFProvider({ children }) {
const [csrfToken, setCsrfToken] = useState(null);
useEffect(() => {
fetch('/api/csrf-token')
.then(r => r.json())
.then(data => setCsrfToken(data.csrfToken));
}, []);
return (
<CSRFContext.Provider value={csrfToken}>
{children}
</CSRFContext.Provider>
);
}
// 使用
function useCSRF() {
return useContext(CSRFContext);
}
Axios拦截器集成
JavaScript
import axios from 'axios';
const api = axios.create();
// 请求拦截器自动添加CSRF Token
api.interceptors.request.use(config => {
const csrfToken = getCsrfFromCookie();
if (csrfToken) {
config.headers['X-CSRF-Token'] = csrfToken;
}
return config;
});
安全配置检查清单
JavaScript
// 完整安全配置示例
app.use(session({
secret: process.env.SESSION_SECRET,
cookie: {
httpOnly: true, // 防止XSS读取
secure: true, // 仅HTTPS传输
sameSite: 'strict' // CSRF防护
}
}));
// 关键操作必须使用POST/PUT/DELETE
app.post('/api/transfer', csrfProtection, handleTransfer);
注意:GET请求不应执行状态变更操作,CSRF Token验证仅针对状态变更请求。
要点总结
| 方案 | 安全性 | 兼容性 | 推荐程度 |
|---|---|---|---|
| CSRF Token | 高 | 好 | ⭐⭐⭐⭐⭐ |
| SameSite Cookie | 高 | IE不支持 | ⭐⭐⭐⭐ |
| Referer检查 | 中 | 好 | ⭐⭐⭐ |
| 双重Cookie | 中高 | 好 | ⭐⭐⭐⭐ |
核心原则:
- 所有状态变更请求必须验证CSRF Token
- 优先使用SameSite=strict Cookie
- 敏感操作增加二次确认
- 定期审计安全配置
📝 发现内容有误?点击此处直接编辑