前端框架中的 CSRF 防护
CSRF攻击的基本原理
CSRF(Cross-Site Request Forgery)跨站请求伪造是一种常见的Web安全威胁。攻击者诱导用户访问恶意网站时,该网站会向用户已认证的目标网站发起请求,利用用户在目标网站的登录状态完成非法操作。这种攻击之所以能够成功,是因为浏览器会自动携带目标网站的cookie等认证信息。
典型的CSRF攻击流程:
- 用户登录银行网站,认证信息保存在cookie中
- 用户访问恶意网站
- 恶意网站包含向银行转账的请求
- 浏览器自动携带银行网站的cookie发送请求
- 银行服务器认为这是合法请求并执行操作
<!-- 恶意网站中的攻击代码 -->
<img src="https://bank.com/transfer?to=attacker&amount=10000" width="0" height="0">
前端框架的CSRF防护机制
现代前端框架提供了多种内置或推荐的CSRF防护方案。这些机制主要围绕验证请求来源和添加随机令牌展开。
1. 同源策略与CORS
浏览器同源策略是防御CSRF的第一道防线。前端框架在处理跨域请求时会严格遵守CORS规范:
// Axios配置示例
axios.defaults.withCredentials = true;
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
重要配置项:
withCredentials
: 控制是否发送凭据Access-Control-Allow-Origin
: 指定允许的源Access-Control-Allow-Credentials
: 是否允许发送cookie
2. CSRF令牌机制
主流框架推荐的防护方案是在表单或请求中添加CSRF令牌:
React实现示例
// 服务端生成并注入令牌
const csrfToken = generateToken();
// 前端组件
function TransferForm() {
return (
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value={csrfToken} />
{/* 其他表单字段 */}
</form>
);
}
Vue实现示例
// 全局拦截器设置
axios.interceptors.request.use(config => {
config.headers['X-CSRF-TOKEN'] = getCSRFToken();
return config;
});
3. 双重Cookie验证
现代框架支持通过双重cookie增强防护:
// 设置HttpOnly的认证cookie
document.cookie = `auth_token=${token}; HttpOnly; Secure`;
// 同时设置可读的CSRF token
document.cookie = `csrf_token=${csrfToken}; SameSite=Strict`;
框架特定实现方案
Angular的CSRF防护
Angular内置了XSRF防护机制,默认从cookie读取XSRF-TOKEN
并添加到请求头:
// 服务端设置cookie
res.cookie('XSRF-TOKEN', token, { secure: true });
// Angular自动处理
this.http.post('/api/transfer', data).subscribe();
React的防护实践
React应用通常结合Redux管理CSRF状态:
// Redux store初始化
const store = createStore(reducer, {
csrf: {
token: window.initialData.csrfToken,
timestamp: Date.now()
}
});
// 高阶组件封装
function withCSRF(Component) {
return function WrappedComponent(props) {
const csrf = useSelector(state => state.csrf);
return <Component {...props} csrf={csrf} />;
}
}
Vue的解决方案
Vue生态通常使用vue-resource或axios插件:
// Vue插件实现
const CSRFPlugin = {
install(Vue) {
Vue.prototype.$csrf = {
getToken() {
return localStorage.getItem('csrfToken');
},
refresh() {
return axios.get('/csrf-token').then(res => {
localStorage.setItem('csrfToken', res.data.token);
});
}
};
}
};
高级防护策略
1. 请求头校验
除了令牌,还可以验证特定请求头:
// 自定义请求头校验
app.use((req, res, next) => {
if (req.get('X-Request-Source') !== 'web-app') {
return res.status(403).send('Invalid request source');
}
next();
});
2. 行为验证
关键操作前增加二次验证:
// React二次确认对话框
function ConfirmTransfer({ amount, recipient }) {
const [confirmed, setConfirmed] = useState(false);
return (
<div>
{!confirmed && (
<dialog open>
<p>确认向{recipient}转账{amount}元?</p>
<button onClick={() => setConfirmed(true)}>确认</button>
</dialog>
)}
{confirmed && <TransferForm />}
</div>
);
}
3. SameSite Cookie属性
现代浏览器支持SameSite属性:
// 严格模式Cookie设置
Set-Cookie: sessionid=xxxx; SameSite=Strict; Secure
实际应用中的注意事项
- 令牌存储:避免将CSRF令牌存储在localStorage中,推荐使用HttpOnly Cookie
- 令牌刷新:重要操作前应刷新CSRF令牌
- 错误处理:统一处理403状态码,引导用户重新认证
- 日志记录:记录CSRF验证失败的请求用于安全分析
// 错误处理中间件
app.use((err, req, res, next) => {
if (err.code === 'EBADCSRFTOKEN') {
securityLogger.warn(`CSRF验证失败: ${req.ip}`);
return res.status(403).render('error/csrf');
}
next(err);
});
性能与安全的平衡
CSRF防护可能影响应用性能,需要合理优化:
- 令牌缓存:对静态资源请求免除CSRF验证
- 域白名单:配置可信域减少验证开销
- 懒验证:非敏感操作延迟验证
# Nginx配置示例
location ~ ^/api/secure/ {
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
}
location ~ ^/static/ {
expires 1y;
add_header Cache-Control "public";
}
测试与验证
完善的测试方案确保防护有效性:
// Jest测试用例
describe('CSRF防护', () => {
test('应拒绝缺少CSRF头的POST请求', async () => {
const res = await request(app)
.post('/transfer')
.expect(403);
});
test('应接受有效CSRF头的请求', async () => {
const token = await getCSRFToken();
const res = await request(app)
.post('/transfer')
.set('X-CSRF-TOKEN', token)
.expect(200);
});
});
与其他安全措施的协同
CSRF防护需要与其他安全机制配合:
- CSP策略:限制外部资源加载
- XSS防护:防止令牌被盗取
- 速率限制:阻止暴力攻击
<!-- CSP策略示例 -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline'">
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:双重提交 Cookie 方案
下一篇:自动化检测与工具推荐