263 lines
5.9 KiB
Markdown
263 lines
5.9 KiB
Markdown
|
|
# 🔐 CSRF保护配置指南
|
|||
|
|
|
|||
|
|
## 📋 问题描述
|
|||
|
|
|
|||
|
|
KaMiXiTong系统在移除`csrf.exempt(api_bp)`后,所有API请求都受到CSRF保护,导致前端API调用返回400错误。
|
|||
|
|
|
|||
|
|
**错误表现**:
|
|||
|
|
```
|
|||
|
|
Failed to load resource: the server responded with a status of 400 (BAD REQUEST)
|
|||
|
|
API request failed: Error: 400: BAD REQUEST
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## ✅ 解决方案
|
|||
|
|
|
|||
|
|
### 当前配置
|
|||
|
|
|
|||
|
|
我们已恢复对API蓝图的CSRF豁免:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# app/__init__.py
|
|||
|
|
from app.api import api_bp
|
|||
|
|
app.register_blueprint(api_bp, url_prefix=f'/api/{app.config["API_VERSION"]}')
|
|||
|
|
csrf.exempt(api_bp) # 对API豁免CSRF
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 配置原理
|
|||
|
|
|
|||
|
|
#### 1. API豁免CSRF的原因
|
|||
|
|
|
|||
|
|
- ✅ **API有独立的认证机制** - 需要登录token或session
|
|||
|
|
- ✅ **非浏览器端调用** - API主要供前端AJAX调用
|
|||
|
|
- ✅ **CORS已配置** - 跨域访问已控制
|
|||
|
|
- ✅ **其他安全措施** - 频率限制、参数验证等
|
|||
|
|
|
|||
|
|
#### 2. Web表单保持CSRF保护
|
|||
|
|
|
|||
|
|
- ✅ **浏览器表单** - 仍然需要CSRF token
|
|||
|
|
- ✅ **用户登录** - 需要CSRF保护
|
|||
|
|
- ✅ **敏感操作** - 需要额外验证
|
|||
|
|
|
|||
|
|
## 🔒 安全策略
|
|||
|
|
|
|||
|
|
### 分层防护
|
|||
|
|
|
|||
|
|
| 组件 | CSRF保护 | 其他保护措施 |
|
|||
|
|
|------|----------|--------------|
|
|||
|
|
| **API接口** | ❌ 豁免 | ✅ 登录验证、频率限制、参数验证 |
|
|||
|
|
| **Web表单** | ✅ 启用 | ✅ CSRF token、表单验证 |
|
|||
|
|
| **第三方回调** | ❌ 豁免 | ✅ 签名验证、IP白名单 |
|
|||
|
|
|
|||
|
|
### 详细配置
|
|||
|
|
|
|||
|
|
#### API安全措施
|
|||
|
|
|
|||
|
|
1. **登录验证**
|
|||
|
|
```python
|
|||
|
|
@api_bp.route('/licenses', methods=['POST'])
|
|||
|
|
@require_login # 要求登录
|
|||
|
|
def create_license():
|
|||
|
|
# 需要管理员权限
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **频率限制**
|
|||
|
|
```python
|
|||
|
|
@api_bp.route('/licenses/generate', methods=['POST'])
|
|||
|
|
@rate_limit(limit=10, window=3600) # 每小时10次
|
|||
|
|
def generate_license():
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **参数验证**
|
|||
|
|
```python
|
|||
|
|
from flask_wtf import FlaskForm
|
|||
|
|
from wtforms import IntegerField, StringField
|
|||
|
|
|
|||
|
|
class LicenseForm(FlaskForm):
|
|||
|
|
product_id = StringField('产品ID', validators=[DataRequired()])
|
|||
|
|
valid_days = IntegerField('有效天数', validators=[DataRequired()])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Web表单CSRF保护
|
|||
|
|
|
|||
|
|
1. **模板中自动包含CSRF token**
|
|||
|
|
```html
|
|||
|
|
<form method="POST">
|
|||
|
|
{{ csrf_token() }} # Flask-WTF自动添加
|
|||
|
|
</form>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **AJAX请求添加CSRF token**
|
|||
|
|
```javascript
|
|||
|
|
// 获取CSRF token
|
|||
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
|
|||
|
|
|
|||
|
|
// AJAX请求头
|
|||
|
|
fetch('/api/data', {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'X-CSRFToken': csrfToken
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(data)
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🛡️ 安全检查
|
|||
|
|
|
|||
|
|
### 已实施的安全措施
|
|||
|
|
|
|||
|
|
- [x] API豁免CSRF(当前配置)
|
|||
|
|
- [x] Web表单保持CSRF保护
|
|||
|
|
- [x] API需要登录验证
|
|||
|
|
- [x] API频率限制
|
|||
|
|
- [x] 参数验证
|
|||
|
|
- [x] CORS配置
|
|||
|
|
- [x] 数据库约束
|
|||
|
|
|
|||
|
|
### 需要注意的安全点
|
|||
|
|
|
|||
|
|
1. **不要在API中信任客户端数据**
|
|||
|
|
- 始终验证输入参数
|
|||
|
|
- 使用参数化查询
|
|||
|
|
- 实施白名单验证
|
|||
|
|
|
|||
|
|
2. **API访问控制**
|
|||
|
|
- 确保所有API都需要认证
|
|||
|
|
- 实施基于角色的访问控制
|
|||
|
|
- 记录API访问日志
|
|||
|
|
|
|||
|
|
3. **第三方接口**
|
|||
|
|
- 支付宝回调等需要单独处理
|
|||
|
|
- 使用签名验证
|
|||
|
|
- 限制IP白名单
|
|||
|
|
|
|||
|
|
## 🔧 配置选项
|
|||
|
|
|
|||
|
|
### 选项1: API豁免CSRF(推荐)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 当前配置 - API豁免CSRF
|
|||
|
|
csrf.exempt(api_bp)
|
|||
|
|
|
|||
|
|
# 优点
|
|||
|
|
- 简单可靠
|
|||
|
|
- 不影响前端开发
|
|||
|
|
- API有其他安全措施
|
|||
|
|
|
|||
|
|
# 适用场景
|
|||
|
|
- 内部API系统
|
|||
|
|
- 有完整认证机制
|
|||
|
|
- 前后端分离架构
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 选项2: API启用CSRF
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 启用API CSRF保护
|
|||
|
|
# 需要修改前端代码
|
|||
|
|
|
|||
|
|
# 前端需要
|
|||
|
|
1. 获取CSRF token
|
|||
|
|
2. 在每个POST/PUT/DELETE请求中添加token
|
|||
|
|
3. 处理token过期逻辑
|
|||
|
|
|
|||
|
|
# 缺点
|
|||
|
|
- 复杂的前端实现
|
|||
|
|
- 需要处理token刷新
|
|||
|
|
- 可能影响API可用性
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 选项3: 混合模式
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 仅对敏感API启用CSRF
|
|||
|
|
from flask_wtf.csrf import csrf_exempt
|
|||
|
|
|
|||
|
|
# 部分API豁免
|
|||
|
|
csrf.exempt(api_bp)
|
|||
|
|
|
|||
|
|
# 特定端点保护
|
|||
|
|
@api_bp.route('/sensitive', methods=['POST'])
|
|||
|
|
@csrf.protect() # 强制启用CSRF
|
|||
|
|
def sensitive_operation():
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🚀 验证配置
|
|||
|
|
|
|||
|
|
### 测试API豁免CSRF
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 直接调用API(不需要CSRF token)
|
|||
|
|
curl -X POST http://localhost:5000/api/v1/licenses \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"product_id": "test", "valid_days": 30}'
|
|||
|
|
|
|||
|
|
# 应该返回401(未认证)而不是400(CSRF错误)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 测试Web表单CSRF
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<!-- 不包含CSRF token的表单应该被拒绝 -->
|
|||
|
|
<form method="POST">
|
|||
|
|
<input type="text" name="username">
|
|||
|
|
<input type="password" name="password">
|
|||
|
|
<!-- 没有{{ csrf_token() }} -->
|
|||
|
|
<button type="submit">登录</button>
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<!-- 返回400错误 -->
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📊 风险评估
|
|||
|
|
|
|||
|
|
### 当前配置的安全性
|
|||
|
|
|
|||
|
|
| 风险类型 | 风险等级 | 缓解措施 |
|
|||
|
|
|----------|----------|----------|
|
|||
|
|
| CSRF攻击 | 🟡 中等 | API有认证机制 |
|
|||
|
|
| XSS攻击 | 🟢 低 | 输入转义、输出编码 |
|
|||
|
|
| SQL注入 | 🟢 低 | 参数化查询 |
|
|||
|
|
| 会话劫持 | 🟡 中等 | HTTPS、Secure Cookie |
|
|||
|
|
| API滥用 | 🟡 中等 | 频率限制、认证 |
|
|||
|
|
|
|||
|
|
### 建议的安全增强
|
|||
|
|
|
|||
|
|
1. **启用HTTPS** - 防止中间人攻击
|
|||
|
|
2. **使用JWT token** - 替代session认证
|
|||
|
|
3. **API签名验证** - 对敏感操作增加签名
|
|||
|
|
4. **IP白名单** - 限制API访问来源
|
|||
|
|
5. **审计日志** - 记录所有API调用
|
|||
|
|
|
|||
|
|
## 📞 支持与联系
|
|||
|
|
|
|||
|
|
### 常见问题
|
|||
|
|
|
|||
|
|
**Q: API返回400错误怎么办?**
|
|||
|
|
A: 检查是否恢复了`csrf.exempt(api_bp)`配置
|
|||
|
|
|
|||
|
|
**Q: 如何为特定API启用CSRF?**
|
|||
|
|
A: 使用`@csrf.protect()`装饰器
|
|||
|
|
|
|||
|
|
**Q: Web表单CSRF验证失败?**
|
|||
|
|
A: 确保模板包含`{{ csrf_token() }}`
|
|||
|
|
|
|||
|
|
**Q: AJAX请求如何添加CSRF token?**
|
|||
|
|
A: 从meta标签获取token并添加到请求头
|
|||
|
|
|
|||
|
|
### 工具脚本
|
|||
|
|
|
|||
|
|
- `scripts/fix_logging.py` - 修复日志问题
|
|||
|
|
- `scripts/health_check.py` - 健康检查
|
|||
|
|
|
|||
|
|
### 相关文档
|
|||
|
|
|
|||
|
|
- [Flask-WTF CSRF文档](https://flask-wtf.readthedocs.io/)
|
|||
|
|
- [OWASP CSRF指南](https://owasp.org/www-community/attacks/csrf)
|
|||
|
|
- [系统优化报告](./SYSTEM_OPTIMIZATION_REPORT.md)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**更新时间**: 2025-12-12
|
|||
|
|
**配置状态**: ✅ API豁免CSRF,Web表单保护
|