修复多项安全漏洞
安全修复清单: 1. 验证码改为图片方式返回,防止明文泄露 2. CORS配置从环境变量读取,不再使用通配符"*" 3. VIP API添加@admin_required装饰器,统一认证 4. 用户登录统一错误消息,防止用户枚举 5. IP限流不再信任X-Forwarded-For头,防止伪造绕过 6. 密码强度要求提升(8位+字母+数字) 7. 日志不���记录完整session/cookie内容,防止敏感信息泄露 8. XSS防护:日志输出和Bug反馈内容转义HTML 9. SQL注入防护:LIKE查询参数转义 10. 路径遍历防护:截图目录白名单验证 11. 验证码重放防护:验证前删除验证码 12. 数据库连接池健康检查 13. 正则DoS防护:限制数字匹配长度 14. Account类密码私有化,__repr__不暴露密码 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
49
app_utils.py
49
app_utils.py
@@ -282,7 +282,11 @@ def check_user_ownership(user_id: int, resource_type: str,
|
||||
|
||||
def verify_and_consume_captcha(session_id: str, code: str, captcha_storage: dict, max_attempts: int = 5) -> Tuple[bool, str]:
|
||||
"""
|
||||
验证并消费验证码
|
||||
验证并消费验证码(安全增强版)
|
||||
|
||||
安全特性:
|
||||
- 先删除验证码再验证,防止重放攻击
|
||||
- 异常情况下也确保验证码被删除
|
||||
|
||||
Args:
|
||||
session_id: 验证码会话ID
|
||||
@@ -307,30 +311,37 @@ def verify_and_consume_captcha(session_id: str, code: str, captcha_storage: dict
|
||||
"""
|
||||
import time
|
||||
|
||||
# 安全修复:先取出并删除验证码,无论验证是否成功都不能重用
|
||||
captcha_data = captcha_storage.pop(session_id, None)
|
||||
|
||||
# 检查验证码是否存在
|
||||
if session_id not in captcha_storage:
|
||||
if captcha_data is None:
|
||||
return False, "验证码已过期或不存在,请重新获取"
|
||||
|
||||
captcha_data = captcha_storage[session_id]
|
||||
try:
|
||||
# 检查过期时间
|
||||
if captcha_data["expire_time"] < time.time():
|
||||
return False, "验证码已过期,请重新获取"
|
||||
|
||||
# 检查过期时间
|
||||
if captcha_data["expire_time"] < time.time():
|
||||
del captcha_storage[session_id]
|
||||
return False, "验证码已过期,请重新获取"
|
||||
# 检查尝试次数
|
||||
if captcha_data.get("failed_attempts", 0) >= max_attempts:
|
||||
return False, f"验证码错误次数过多({max_attempts}次),请重新获取"
|
||||
|
||||
# 检查尝试次数
|
||||
if captcha_data.get("failed_attempts", 0) >= max_attempts:
|
||||
del captcha_storage[session_id]
|
||||
return False, f"验证码错误次数过多({max_attempts}次),请重新获取"
|
||||
# 验证代码(不区分大小写)
|
||||
if captcha_data["code"].lower() != code.lower():
|
||||
# 验证失败,增加失败计数后放回(允许继续尝试)
|
||||
captcha_data["failed_attempts"] = captcha_data.get("failed_attempts", 0) + 1
|
||||
# 只有未超过最大尝试次数才放回
|
||||
if captcha_data["failed_attempts"] < max_attempts:
|
||||
captcha_storage[session_id] = captcha_data
|
||||
return False, "验证码错误"
|
||||
|
||||
# 验证代码(不区分大小写)
|
||||
if captcha_data["code"].lower() != code.lower():
|
||||
captcha_data["failed_attempts"] = captcha_data.get("failed_attempts", 0) + 1
|
||||
return False, "验证码错误"
|
||||
|
||||
# 验证成功,删除验证码(防止重复使用)
|
||||
del captcha_storage[session_id]
|
||||
return True, "验证成功"
|
||||
# 验证成功,验证码已被删除,不会被重用
|
||||
return True, "验证成功"
|
||||
except Exception as e:
|
||||
# 异常情况下确保验证码不会被重用(已在函数开头删除)
|
||||
logger.error(f"验证码验证异常: {e}")
|
||||
return False, "验证码验证失败,请重新获取"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user