security: 全面安全加固 v2.1

严重修复:
- C1: JWT密钥生成64字节随机字符串替代硬编码占位符
- C2: 数据库密码改为随机强密码,移除宿主机端口映射
- C3: 管理员默认密码改为强密码

高危修复:
- H1: 移除数据库54326端口映射,仅内部网络访问
- H3: 添加X-Frame-Options/CSP/XSS-Protection等安全头
- H4: 前端所有innerHTML拼接处添加escapeHtml转义防XSS
- H5: SMTP密码在API响应中脱敏显示
- H6: 债务列表接口添加用户隔离,普通用户只能看自己的数据
- H7: 债务详情/计划/记录接口添加归属校验
- H8: 账户级暴力破解锁定(5次失败锁定5分钟)
- H9: 速率限制数据增加过期清理机制防内存泄漏

中危修复:
- M1: 注册/重置密码接口添加后端密码强度校验(最少8位)
- M4: 添加全局异常处理器,500错误不再暴露堆栈
- M5: Docker容器改为非root用户运行
- M6: 添加.dockerignore排除pyc和敏感文件
- M7: 管理员操作写入审计日志
- M8: CORS限制为实际使用的HTTP方法和头

其他:
- Nginx隐藏版本号(server_tokens off)
- 管理员角色修改添加枚举校验
- 密码重置验证码接口返回统一错误信息
This commit is contained in:
OP
2026-06-28 22:46:24 +08:00
parent ead41e2b8e
commit 498d29dfa9
13 changed files with 103 additions and 31 deletions

View File

@@ -42,6 +42,8 @@ async def list_debts(
db: AsyncSession = Depends(get_db),
):
q = select(Debt)
if user.role != "admin":
q = q.where(Debt.user_id == user.id)
if status:
q = q.where(Debt.status == status)
if creditor:
@@ -89,6 +91,8 @@ async def get_debt(
debt = result.scalar_one_or_none()
if not debt:
raise HTTPException(status_code=404, detail="债务不存在")
if debt.user_id != user.id and user.role != "admin":
raise HTTPException(status_code=403, detail="无权访问此债务")
return await _enrich(db, debt)
@@ -153,8 +157,11 @@ async def get_plans(
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(Debt).where(Debt.id == debt_id))
if not result.scalar_one_or_none():
debt = result.scalar_one_or_none()
if not debt:
raise HTTPException(status_code=404, detail="债务不存在")
if debt.user_id != user.id and user.role != "admin":
raise HTTPException(status_code=403, detail="无权访问此债务")
pr = await db.execute(select(RepaymentPlan).where(RepaymentPlan.debt_id == debt_id).order_by(RepaymentPlan.due_date))
return pr.scalars().all()
@@ -166,7 +173,10 @@ async def get_records(
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(Debt).where(Debt.id == debt_id))
if not result.scalar_one_or_none():
debt = result.scalar_one_or_none()
if not debt:
raise HTTPException(status_code=404, detail="债务不存在")
if debt.user_id != user.id and user.role != "admin":
raise HTTPException(status_code=403, detail="无权访问此债务")
rr = await db.execute(select(RepaymentRecord).where(RepaymentRecord.debt_id == debt_id).order_by(RepaymentRecord.paid_date.desc()))
return rr.scalars().all()