security: harden proxy IP trust, token flow, health and sessions

This commit is contained in:
2026-02-09 09:14:47 +08:00
parent f645a0f8ea
commit ebfac7266b
7 changed files with 199 additions and 79 deletions

View File

@@ -15,7 +15,7 @@ import email_service
from app_config import get_config
from app_logger import get_logger
from app_security import get_rate_limit_ip, require_ip_not_locked, validate_email, validate_password, validate_username
from flask import Blueprint, jsonify, request
from flask import Blueprint, jsonify, request, session
from flask_login import login_required, login_user, logout_user
from routes.pages import render_app_spa_or_legacy
from services.accounts_service import load_user_accounts
@@ -279,19 +279,38 @@ def register():
@api_auth_bp.route("/api/verify-email/<token>")
def verify_email(token):
"""验证邮箱 - 用户点击邮件中的链接"""
result = email_service.verify_email_token(token, email_service.EMAIL_TYPE_REGISTER)
result = email_service.verify_email_token(token, email_service.EMAIL_TYPE_REGISTER, consume=False)
if result:
token_id = result["token_id"]
user_id = result["user_id"]
email = result["email"]
database.approve_user(user_id)
if not database.approve_user(user_id):
logger.error(f"用户邮箱验证失败: 用户审核更新失败 user_id={user_id}, email={email}")
error_message = "验证处理失败,请稍后重试"
spa_initial_state = {
"page": "verify_result",
"success": False,
"title": "验证失败",
"error_message": error_message,
"primary_label": "返回登录",
"primary_url": "/login",
}
return render_app_spa_or_legacy(
"verify_failed.html",
legacy_context={"error_message": error_message},
spa_initial_state=spa_initial_state,
)
system_config = database.get_system_config()
auto_approve_vip_days = system_config.get("auto_approve_vip_days", 7)
if auto_approve_vip_days > 0:
database.set_user_vip(user_id, auto_approve_vip_days)
if not email_service.consume_email_token(token_id):
logger.warning(f"用户邮箱验证后Token消费失败: token_id={token_id}, user_id={user_id}")
logger.info(f"用户邮箱验证成功: user_id={user_id}, email={email}")
spa_initial_state = {
"page": "verify_result",
@@ -499,6 +518,11 @@ def reset_password_confirm():
@api_auth_bp.route("/api/generate_captcha", methods=["POST"])
def generate_captcha():
"""生成4位数字验证码图片"""
client_ip = get_rate_limit_ip()
allowed, error_msg = check_ip_request_rate(client_ip, "login")
if not allowed:
return jsonify({"error": error_msg}), 429
session_id = str(uuid.uuid4())
code = "".join(str(secrets.randbelow(10)) for _ in range(4))
@@ -578,4 +602,7 @@ def login():
def logout():
"""用户登出"""
logout_user()
session.pop("admin_id", None)
session.pop("admin_username", None)
session.pop("admin_reauth_until", None)
return jsonify({"success": True})