CSRF跨站请求伪造攻击原理与防御
概述
跨站请求伪造(Cross-Site Request Forgery,简称 CSRF)是一种常见且危害严重的网络安全攻击技术。攻击者通过伪造用户的请求,利用受害者在目标网站的登录态,诱导用户在不知情的情况下执行非法操作。CSRF攻击广泛存在于WEB应用中,尤其对具有权限操作(如用户信息修改、资金转账、发帖等)的接口威胁巨大。
本文将系统剖析CSRF攻击的原理,结合实战环境还原攻击场景,详解多种防御机制,并辅以代码示例,帮助具备一定基础的技术人员深化理解与实践防御能力。
原理分析
CSRF攻击的核心概念
CSRF攻击基于“用户浏览器自动携带身份凭证”的特性。当客户端登录某网站后,通常会保持登录态(Cookies、Session等),此时所有对该网站的请求都会自动携带有效身份信息。攻击者在另一个恶意网站中植入伪造请求,将这请求发送给目标网站。如果目标网站未做有效防护,将误以为是合法用户发起的操作,从而执行攻击者所设计的非法动作。
攻击流程
- 受害者登录受信任网站(目标网站),浏览器获得并保留登录凭据(如Cookie)。
- 受害者访问攻击者控制的恶意网站。
- 恶意网站构造一个HTTP请求指向目标网站敏感接口,如修改密码、转账等。
- 浏览器自动带上目标网站的Cookie等凭证,发送请求。
- 目标网站接收到请求,并验证凭证后执行操作,从而实现攻击。
CSRF攻击条件总结
- 有状态会话机制:目标网站依赖Cookie或其他认证凭证识别用户。
- 自动携带凭证:浏览器遵照同源策略之外,请求自动带上Cookie。
- 缺少请求验证机制:服务器端未能准确判断请求是否由用户真实操作发起。
CSRF 与 XSS 的区别
- CSRF利用的是受害者的身份权限,通过跨站请求执行操作。
- **XSS(跨站脚本攻击)**则是攻击者在目标网站注入恶意脚本,直接控制用户浏览器执行恶意代码。
实战步骤
环境准备
假设存在一个简单的电商网站,其用户登录后能够修改收货地址(POST请求 /user/update_address)。该接口未做CSRF防护。
实际攻击示例
1. 伪造请求结构
恶意站点插入如下HTML:
<form action="https://targetsite.com/user/update_address" method="POST" id="csrfForm">
<input type="hidden" name="address" value="攻击者地址信息" />
</form>
<script>
document.getElementById('csrfForm').submit();
</script>
2. 攻击原理演示
- 受害者登录 targetsite.com ,获得登录态Cookie。
- 访问攻击站点,自动触发伪造请求。
- 浏览器带上目标站点Cookie,服务端误以为是合法操作,修改用户地址。
3. 验证漏洞
查看用户地址是否被非法更改,即为CSRF攻击成功。
代码示例:Token防御机制
最常用且有效的防御措施是使用同步请求防御令牌CSRF Token。
服务器端生成Token(示例基于Node.js/Express)
const express = require('express');
const crypto = require('crypto');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));
// 中间件生成CSRF Token
app.use((req, res, next) => {
if (!req.cookies.csrfToken) {
const token = crypto.randomBytes(24).toString('hex');
res.cookie('csrfToken', token, { httpOnly: true, sameSite: 'strict' });
req.csrfToken = token;
} else {
req.csrfToken = req.cookies.csrfToken;
}
next();
});
// 渲染带Token的页面
app.get('/user/profile', (req, res) => {
const formHtml = `
<form action="/user/update_address" method="POST">
<input name="address" type="text" />
<input type="hidden" name="csrfToken" value="${req.csrfToken}" />
<button type="submit">更新地址</button>
</form>
`;
res.send(formHtml);
});
// 接收请求时验证Token
app.post('/user/update_address', (req, res) => {
const userToken = req.body.csrfToken;
const cookieToken = req.cookies.csrfToken;
if (!userToken || userToken !== cookieToken) {
return res.status(403).send('CSRF Token 不匹配,禁止操作');
}
// 处理地址更新逻辑
res.send('地址更新成功');
});
app.listen(3000);
说明
- 服务端生成Token存于HTTP Only Cookie中,只读不暴露给JS,防止XSS窃取。
- 前端通过隐藏表单域提交Token,服务端比对请求参数和Cookie中Token是否一致。
- 不一致即阻断,能有效防止伪造请求提交。
防御建议
1. CSRF Token
- 每个用户会话生成唯一且难以预测的Token。
- Token需与请求一起提交(隐藏域、请求头)。
- 服务器严格验证。
- 结合SameSite Cookie 属性增强防护。
2. 使用 SameSite Cookie 属性
SameSite=Lax:禁止跨站提交影 响登录数据提交。SameSite=Strict:更严格,同站请求才发送 Cookie,但提升用户体验时需谨慎使用。
3. 双重提交Cookie(Double Submit Cookie)
- 服务器发放Token存于Cookie,客户端从Cookie读取Token放入请求中。
- 服务器检验两者是否一致,缺点是Token存于Cookie中,易被XSS攻击。
4. 验证HTTP请求特征
- 检验Referer头 and Origin头部,仅接受来自同源的请求。
- 但部分用户隐私工具会隐藏Referer,误判风险较大。
5. 采用非幂等请求协议
- 对关键操作,推荐使用POST且附加验证码、二次确认等防护措施。
- 避免将敏感操作暴露为GET请求。
6. 前端框架防护
- 现代前端框架(如React、Vue等)和HTTP库(Axios、Fetch)通常支持自动附加CSRF Token。
- 结合后端验证,形成闭环。
总结
CSRF是一种依赖用户会话机制和浏览器自动携带凭证的跨站攻击,易于被忽视但危害巨大。防护的关键在于对请求有效性进行验证,常用且实践成熟的方案是同步令牌机制(CSRF Token),结合HTTP头校验与安全Cookie策略。作为安全专家,务必在设计应用接口时,从架构和实现层面系统性部署CSRF防护,绝不可将安全留白。
随着Web生态演进,CDN、浏览器策略和框架更新也会影响CSRF防御策略的选型和配置,应持续关注行业动态和最佳实践,不断增强应用安全硬度。
参考阅读(可自选):
- OWASP官方CSRF防御指南
- 同源策略与Cookie安全架构详解
- Web应用安全最佳实践白皮书