目录
[1. GET型CSRF](#1. GET型CSRF)
[2. POST型CSRF](#2. POST型CSRF)
[3. 复杂CSRF(JSON/XML)](#3. 复杂CSRF(JSON/XML))
[案例1:GitHub CSRF漏洞(2018)](#案例1:GitHub CSRF漏洞(2018))
[案例2:Netflix CSRF(2013)](#案例2:Netflix CSRF(2013))
[防御层2:SameSite Cookie属性](#防御层2:SameSite Cookie属性)
[1. Django的CSRF中间件](#1. Django的CSRF中间件)
[2. Spring Security的CSRF防护](#2. Spring Security的CSRF防护)
[3. Express.js的csurf中间件](#3. Express.js的csurf中间件)
[4. React + 现代API的防护](#4. React + 现代API的防护)
[1. 子域CSRF攻击](#1. 子域CSRF攻击)
[2. 登录CSRF](#2. 登录CSRF)
[3. JSON CSRF与CORS](#3. JSON CSRF与CORS)
[4. 混合内容与协议降级](#4. 混合内容与协议降级)
[1. SameSite Cookie的普及](#1. SameSite Cookie的普及)
[2. 基于机制而非令牌的新方案](#2. 基于机制而非令牌的新方案)
[3. 联邦学习与行为分析](#3. 联邦学习与行为分析)
[4. WebAuthn与无密码认证](#4. WebAuthn与无密码认证)
[5. 标准化协议增强](#5. 标准化协议增强)
引言:为何登录银行后浏览一个论坛可能让资金被盗?
想象一个场景:您刚刚登录了网上银行,在另一个标签页打开了一个看似正常的论坛。论坛中隐藏着一张图片,当您的浏览器加载它时,悄无声息地向银行发送了一个转账请求。因为您已经登录,银行认为这是您的合法操作------资金就这样不翼而飞。这就是CSRF攻击的典型危害:利用用户已建立的信任会话,在用户不知情的情况下执行恶意操作。
第一章:CSRF的本质与核心概念
什么是CSRF?
跨站请求伪造(CSRF,也写作XSRF) 是一种Web安全漏洞,攻击者诱使已认证用户在不知情、未授权的情况下,以该用户的身份执行非本意的操作。
核心特征:
-
利用已建立的信任 :浏览器与目标网站间的有效会话
-
用户无感知:攻击发生在后台,用户可能完全不知情
-
请求来自合法用户:目标网站看到的是真实用户的合法请求
-
攻击者无需获取响应:通常只关心请求是否成功执行
与其他攻击的区别:
| 特性 | CSRF | XSS |
|---|---|---|
| 目标 | 利用用户的身份执行操作 | 在用户浏览器执行脚本 |
| 所需条件 | 用户已登录目标站点 | 网站存在可注入点 |
| 攻击者视角 | 关注请求执行 | 关注脚本执行结果 |
| 典型危害 | 转账、改密、发帖 | 窃取Cookie、键盘记录 |
第二章:CSRF的攻击原理与流程
攻击三要素:
-
Cookie-based会话:目标网站使用Cookie管理会话
-
无防伪验证:关键操作不验证请求来源
-
可预测请求:攻击者能构造出有效请求
经典攻击流程:
text
1. 用户登录银行网站(example-bank.com)
→ 获得会话Cookie:sessionid=abc123
2. 用户访问攻击者控制的恶意网站
→ 页面包含隐藏表单或自动提交脚本
3. 浏览器自动发送请求到银行
→ 携带用户的合法Cookie:sessionid=abc123
4. 银行验证Cookie有效
→ 执行转账操作,资金转到攻击者账户
浏览器自动携带Cookie的机制:
http
# 登录时银行设置Cookie
Set-Cookie: sessionid=abc123; Domain=.example-bank.com; Path=/
# 之后所有向example-bank.com的请求都会自动包含
Cookie: sessionid=abc123
第三章:CSRF的攻击类型与技术实现
1. GET型CSRF
最简单直接的攻击方式,通常通过<img>、<iframe>等标签自动触发。
示例攻击代码:
html
<!-- 恶意网站中的隐藏图片 -->
<img src="https://example-bank.com/transfer?to=attacker&amount=10000"
width="0" height="0" alt="">
<!-- 或通过iframe -->
<iframe src="https://example-bank.com/delete-account"
style="display:none;"></iframe>
特点:
-
利用浏览器自动加载资源的特性
-
用户只需访问恶意页面即可触发
-
适用于执行幂等操作(如查询、删除)
2. POST型CSRF
通过表单自动提交实现,可发送更多数据。
示例攻击代码:
html
<!-- 隐藏表单 + 自动提交脚本 -->
<form id="csrf-form"
action="https://example-bank.com/transfer"
method="POST"
style="display:none;">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="currency" value="USD">
</form>
<script>
document.getElementById('csrf-form').submit();
</script>
3. 复杂CSRF(JSON/XML)
现代API常用JSON格式,需要特殊处理。
技术难点: 简单表单无法直接发送JSON
绕过方法:
html
<script>
// 通过Fetch API发送JSON请求
fetch('https://api.example.com/update-profile', {
method: 'PUT',
credentials: 'include', // 携带Cookie
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'attacker@evil.com',
is_admin: true
})
});
</script>
条件限制:
-
需要CORS配置允许恶意网站
-
或通过Flash等插件绕过限制(已逐渐淘汰)
第四章:真实世界CSRF攻击案例
案例1:GitHub CSRF漏洞(2018)
-
漏洞点:仓库设置页面
-
攻击方式:恶意页面诱导用户点击后,自动修改仓库设置
-
危害:攻击者可获取仓库控制权
-
修复:GitHub增加了CSRF令牌验证
案例2:Netflix CSRF(2013)
-
漏洞:账户添加配送地址功能
-
攻击:通过CSRF添加攻击者地址到用户账户
-
利用:获取用户物理地址信息,可能用于社会工程学攻击
案例3:YouTube点赞/订阅CSRF
-
历史漏洞:早期YouTube通过简单GET请求处理订阅操作
-
攻击代码:
html
<img src="https://youtube.com/subscribe?channel=attacker_channel"> -
影响:用户访问恶意页面后自动订阅攻击者频道
案例4:路由器CSRF攻击
最具破坏性的应用场景之一:
html
<!-- 重置路由器为出厂设置 -->
<form action="http://192.168.1.1/reset" method="POST">
<input type="hidden" name="confirm" value="true">
</form>
<script>document.forms[0].submit();</script>
<!-- 修改DNS设置 -->
<form action="http://192.168.1.1/set-dns" method="POST">
<input type="hidden" name="dns1" value="8.8.8.8">
<input type="hidden" name="dns2" value="8.8.4.4">
</form>
后果: 完全控制局域网流量,可进行中间人攻击。
第五章:CSRF防御的完整方案
防御层1:令牌验证(最有效方案)
同步令牌模式
python
# 服务器端生成令牌
def generate_csrf_token(user_session):
token = random_string(32)
session['csrf_token'] = token
return token
# 在表单中嵌入
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token"
value="{{ csrf_token }}">
<!-- 其他字段 -->
</form>
# 服务器验证
def validate_csrf(request):
token_in_session = session.get('csrf_token')
token_in_request = request.form.get('csrf_token')
return token_in_session == token_in_request
双重Cookie验证
javascript
// 前端设置自定义请求头
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': getCookie('csrf_token')
}
});
// 服务器验证
app.post('/api/transfer', (req, res) => {
const tokenInCookie = req.cookies.csrf_token;
const tokenInHeader = req.headers['x-csrf-token'];
if (tokenInCookie && tokenInCookie === tokenInHeader) {
// 请求合法
} else {
// 拒绝请求
}
});
防御层2:SameSite Cookie属性
三种模式:
http
# 严格模式 - 完全禁止跨站携带
Set-Cookie: sessionid=abc123; SameSite=Strict
# 宽松模式 - GET请求可跨站携带(推荐平衡方案)
Set-Cookie: sessionid=abc123; SameSite=Lax
# 无限制 - 默认行为,不推荐
Set-Cookie: sessionid=abc123; SameSite=None; Secure
SameSite=Lax的实际效果:
-
✅ 允许:从其他网站点击链接、GET表单提交
-
❌ 阻止:跨站POST表单提交、Fetch API请求
防御层3:来源验证
检查Referer/Origin头部
python
def check_referer(request):
referer = request.headers.get('Referer')
origin = request.headers.get('Origin')
# 允许来自同一源站或受信任源
allowed_origins = ['https://example.com', 'https://trusted.example.com']
if origin in allowed_origins:
return True
# 验证Referer
if referer and referer.startswith('https://example.com/'):
return True
return False
局限性:
-
某些浏览器可能不发送Referer
-
用户可能禁用Referer
-
从HTTPS到HTTP的请求不包含Referer
防御层4:自定义请求头
原理: 利用CORS的简单请求/预检请求机制
javascript
// 所有敏感请求添加自定义头
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// 或使用自定义头
fetch('/api/sensitive-action', {
headers: {
'X-Custom-Header': 'csrf-protection'
}
});
优势:
-
浏览器对非简单请求会发送预检请求
-
攻击者无法通过简单HTML表单添加自定义头
防御层5:用户交互验证
重新认证
-
关键操作前要求重新输入密码
-
示例:转账、修改邮箱、修改密码
CAPTCHA验证
-
在执行敏感操作前要求完成人机验证
-
有效但影响用户体验
二次确认
javascript
// 前端确认
if (confirm('确认要转账10000元吗?')) {
// 发送请求
}
第六章:现代框架的CSRF防护
1. Django的CSRF中间件
python
# settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
# 模板中使用
<form method="post">
{% csrf_token %}
<!-- 表单字段 -->
</form>
# AJAX请求
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
fetch('/api/endpoint', {
method: 'POST',
headers: {
'X-CSRFToken': csrftoken
}
});
2. Spring Security的CSRF防护
java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf() // 默认启用CSRF保护
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
// Thymeleaf模板中自动添加令牌
<form th:action="@{/transfer}" method="post">
<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
</form>
3. Express.js的csurf中间件
javascript
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
// 应用中间件
app.use(csrfProtection);
// 获取令牌
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// 验证令牌
app.post('/process', csrfProtection, (req, res) => {
// 只有有效CSRF令牌能到达这里
});
4. React + 现代API的防护
javascript
// 使用HTTP-only Cookie + 双重提交
import axios from 'axios';
// 从Cookie读取CSRF令牌(需服务器设置为非HTTP-only)
function getCsrfToken() {
return document.cookie
.split('; ')
.find(row => row.startsWith('csrf_token='))
?.split('=')[1];
}
// 设置拦截器
axios.interceptors.request.use(config => {
if (['post', 'put', 'patch', 'delete'].includes(config.method)) {
config.headers['X-CSRF-Token'] = getCsrfToken();
}
return config;
});
第七章:CSRF检测与测试方法
手动测试流程:
-
识别敏感操作
-
用户资料修改
-
密码/邮箱变更
-
金融交易
-
管理操作
-
-
检查防护机制
http
# 检查响应头 Set-Cookie: sessionid=xxx; HttpOnly; Secure; SameSite=Strict # 检查表单中的CSRF令牌 <input type="hidden" name="csrf_token" value="..."> -
构造攻击POC
html
<!-- 简单GET请求测试 --> <img src="https://target.com/delete?id=123"> <!-- POST请求测试 --> <form action="https://target.com/transfer" method="POST" id="csrf"> <input type="hidden" name="amount" value="1000"> <input type="hidden" name="to" value="attacker"> </form> <script>document.getElementById('csrf').submit();</script>
自动化测试工具:
-
Burp Suite Professional:CSRF扫描器
-
OWASP ZAP:自动CSRF检测
-
CSRF Tester浏览器扩展:手动生成测试用例
测试注意事项:
-
会话状态:测试时需保持有效登录会话
-
令牌复杂性:检查令牌是否可预测或复用
-
API端点:RESTful API也需要CSRF防护
-
移动端/桌面应用:检查不同客户端的防护一致性
第八章:特殊场景与边缘案例
1. 子域CSRF攻击
场景: app.example.com 和 user.example.com 共享Cookie
攻击: 从evil.example.com发起的攻击可能成功
防御: 严格设置Cookie的Domain和Path属性
2. 登录CSRF
原理: 攻击者先让用户登录攻击者账户
危害: 用户数据泄露给攻击者
示例:
html
<form action="https://target.com/login" method="POST">
<input type="hidden" name="username" value="attacker">
<input type="hidden" name="password" value="attacker_pass">
</form>
<script>document.forms[0].submit();</script>
3. JSON CSRF与CORS
现代挑战:
javascript
// 恶意网站
fetch('https://api.target.com/update', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ "admin": true })
});
防护方案:
-
严格CORS策略
-
要求自定义请求头
-
验证Content-Type
4. 混合内容与协议降级
HTTPS → HTTP的CSRF
-
现代浏览器已加强防护
-
但仍需确保全站HTTPS
第九章:CSRF防护最佳实践总结
必须实施的措施:
-
✅ 关键操作使用POST/PUT/DELETE而非GET
-
✅ 为会话Cookie设置SameSite属性(至少Lax)
-
✅ 实施CSRF令牌验证(同步令牌模式)
-
✅ 敏感操作要求重新认证
推荐增强措施:
-
🌟 双重提交Cookie模式:前后端分离架构的优选
-
🌟 自定义请求头验证:结合CORS预检请求
-
🌟 操作日志与异常监控:及时发现攻击尝试
-
🌟 定期安全测试:包括CSRF专项测试
按风险等级分层防护:
| 操作风险等级 | 防护措施 |
|---|---|
| 高风险 (转账、改密) | CSRF令牌 + SameSite=Strict + 重新认证 |
| 中风险 (发帖、评论) | CSRF令牌 + SameSite=Lax |
| 低风险 (查询、浏览) | SameSite=Lax(可选) |
技术栈特定建议:
传统Web应用(服务端渲染):
-
框架内置的CSRF中间件
-
每个表单包含CSRF令牌
-
SameSite Cookie + HttpOnly
SPA单页应用:
-
双重Cookie验证模式
-
令牌存储在非HTTP-only Cookie中
-
通过拦截器自动附加到请求
-
严格CORS配置
移动应用/原生应用:
-
使用Bearer Token而非Cookie
-
每个请求携带访问令牌
-
令牌存储在安全存储区
第十章:未来趋势与演进
1. SameSite Cookie的普及
-
Chrome 80+默认将无SameSite标记的Cookie视为Lax
-
推动全行业采用SameSite属性
2. 基于机制而非令牌的新方案
-
Origin-Based防护:严格验证请求来源
-
用户交互证明:要求用户交互(如点击)后才允许敏感操作
3. 联邦学习与行为分析
-
分析用户正常行为模式
-
检测异常请求时序和来源
4. WebAuthn与无密码认证
-
生物识别/硬件密钥认证
-
从根本上改变身份验证模型
5. 标准化协议增强
-
新的HTTP头部提案
-
浏览器内置防护机制强化
结论:CSRF在2024年的现状
CSRF作为经典的Web安全漏洞,在防护措施日益完善的今天,大规模自动化攻击已大幅减少。但并未消失:
-
防护不完全的遗留系统仍然存在风险
-
新型API和架构可能引入新的攻击面
-
用户教育缺失导致社交工程结合CSRF仍然有效
最核心的防御哲学仍然是:
"Never trust a request just because it comes with a valid session cookie."
在现代Web开发中,CSRF防护应作为基础安全基线的一部分,而不是事后考虑。通过框架内置防护、安全配置和深度防御策略,可以以较低成本实现强大的CSRF防护,保护用户免受这种"隐秘而危险"的攻击。