Web安全——CSRF代码审计
字数 1376 2025-08-18 11:39:30
CSRF漏洞原理与防御详解
一、CSRF漏洞概述
跨站请求伪造(Cross-Site Request Forgery, CSRF)是一种Web安全漏洞,攻击者通过技术手段欺骗用户的浏览器去访问已认证的网站并执行非自愿操作。
核心特征
- 利用用户已认证的会话状态
- 在用户不知情的情况下执行操作
- 不需要直接窃取用户凭证
与XSS的区别
| 特征 | XSS | CSRF |
|---|---|---|
| 目标 | 窃取用户数据/会话 | 执行非授权操作 |
| 攻击方式 | 注入恶意脚本 | 伪造请求 |
| 用户感知 | 可能察觉异常 | 完全不知情 |
| 依赖条件 | 需要用户访问恶意页面 | 需要用户已登录目标站点 |
二、CSRF攻击原理
攻击流程
- 用户登录受信任网站A,生成有效会话Cookie
- 用户在不登出A的情况下访问恶意网站B
- 网站B包含伪造A网站请求的代码
- 浏览器自动携带A网站的Cookie发送请求
- A网站服务器认为这是用户的自愿操作
必要条件
- 目标站点存在可预测的操作参数
- 用户已登录目标站点且会话有效
- 用户访问恶意页面
攻击实例分析
以添加后台用户为例:
- 管理员登录后台(生成有效会话)
- 管理员访问恶意页面
- 恶意页面包含自动提交的添加用户表单:
<form action="http://www.csrf.cn/csrf/adduser.php" method="POST">
<input type="hidden" name="username" value="hacker">
<input type="hidden" name="password" value="hacked">
</form>
<script>document.forms[0].submit();</script>
- 浏览器自动提交表单,完成用户添加
三、CSRF漏洞防御策略
1. 验证HTTP Referer字段
- 原理:检查请求来源是否为合法域名
- 实现:
$referer = $_SERVER['HTTP_REFERER']; $parsed = parse_url($referer); if(strpos($parsed['host'], 'bank.test') === false) { die('非法请求来源'); } - 缺点:
- Referer可能被篡改或缺失
- 某些浏览器隐私设置会禁用Referer
2. 请求地址中添加Token并验证
- 原理:为每个表单生成不可预测的令牌
- 实现步骤:
- 服务器生成Token并存入Session
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));- 将Token嵌入表单
<input type="hidden" name="csrf_token" value="<?=$_SESSION['csrf_token']?>">- 提交时验证Token
if($_POST['csrf_token'] !== $_SESSION['csrf_token']) { die('CSRF Token验证失败'); }
3. 自定义HTTP头属性
- 原理:通过AJAX添加自定义头
- 实现:
var token = '...'; // 从cookie或meta标签获取 $.ajaxSetup({ headers: {'X-CSRF-Token': token} }); - 优势:
- 不暴露在URL中
- 不受Referer限制
4. 添加验证码
- 原理:强制用户交互确认
- 适用场景:
- 敏感操作(转账、修改密码)
- 高权限操作
5. SameSite Cookie属性
- 原理:限制第三方上下文发送Cookie
- 设置方法:
session_set_cookie_params([ 'samesite' => 'Strict' // 或'Lax' ]); - 模式:
- Strict: 完全禁止第三方Cookie
- Lax: 允许安全方法(GET)的跨站请求
四、代码审计要点
1. 关键检查点
- 敏感操作是否缺乏CSRF防护
- Token生成是否足够随机
- Token验证逻辑是否正确
- 是否存在宽松的Referer检查
2. 常见漏洞模式
# 漏洞代码示例(无防护)
def transfer(request):
if request.method == 'POST':
# 直接处理请求,无CSRF检查
process_transfer()
return render('transfer.html')
# 安全代码示例(Django框架)
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def transfer(request):
if request.method == 'POST':
# 自动验证CSRF Token
process_transfer()
return render('transfer.html')
五、最佳实践建议
- 敏感操作:必须使用POST方法+CSRF Token
- API设计:RESTful API应使用自定义头Token
- 框架使用:优先使用内置CSRF防护机制
- Django:
{% csrf_token %} - Laravel:
@csrf - Spring Security: 默认启用
- Django:
- 防御组合:采用多层防御策略
- 定期审计:检查Token实现的有效性
六、测试验证方法
-
手工测试:
- 复制合法请求,去除Token重放
- 构造恶意页面触发请求
-
自动化工具:
- OWASP ZAP
- Burp Suite CSRF PoC生成器
- CSRFTester
-
验证要点:
- 能否绕过Token验证
- 是否接受空/重复Token
- Token与会话是否严格绑定