🔴 Admin 严重问题

以下问题可能导致数据泄露、安全风险或资金损失,需优先修复。

🔴 A01 ✅ 已修复 安全 · users 模块

用户密码哈希返回给前端

API:GET /admin/users/:id
源码位置:Financial_Admin/server/routes/admin.js — users 列表查询
问题描述:后端 SELECT 语句包含 password_hash 字段并将完整 bcrypt 哈希值返回给前端。虽然前端可能不渲染该字段,但数据已通过 HTTP 响应传输,存在被中间人攻击或日志泄露的风险。
根因分析:SQL 查询未使用 SELECT columns EXCEPT (password_hash) 或明确列举字段,而是使用了 SELECT * 或包含 password_hash 的完整字段列表。
影响范围:任何能访问 GET /admin/usersGET /admin/users/:id 的 Admin 账户均可获取任意用户的密码哈希。攻击者若拿到哈希可尝试离线暴力破解(bcrypt cost factor 低时更脆弱)。
改进建议:
  1. 后端 SQL 查询明确排除 password_hash
    SELECT id, phone, nickname, ..., created_at FROM users WHERE ...
  2. 或在后端响应前删除该字段:delete result.password_hash
  3. 提高 bcrypt cost factor 至 12
风险评级:高 — 若 admin 账户被攻破,攻击者可批量获取用户密码哈希进行撞库攻击。
🔴 A02 ✅ 已修复 安全 · system 模块

SMTP 密码明文存储在数据库

API:GET /admin/email-config · PUT /admin/email-config
源码位置:Financial_Admin/server/routes/admin.js — email-config 路由
问题描述:SMTP 密码以明文存储在 system_config 表中(key = email_smtp),GET 接口直接将包含密码的完整配置返回给前端。任何 Admin 账户均可通过该接口获取 SMTP 明文密码。
修复进度:
  1. ✅ GET /admin/email-config:返回时将 smtp.pass 替换为 ********
  2. ✅ PUT /admin/email-config:提交密码为 ******** 或空时,保留原密码不更新
  3. ❌ 数据库中仍为明文存储(未做 AES-256 加密)
根因分析:smtp.pass 字段在数据库中未加密,PUT 时也是直接存储明文。生产环境中 SMTP 密码通常是邮箱授权码或重要凭据。
影响范围:获取 SMTP 密码后,攻击者可:以系统名义发送欺诈邮件、监听本应发给用户的通知邮件、利用 SMTP 服务器发送垃圾邮件(产生费用或导致 IP 被封禁)。
改进建议:
  1. SMTP 密码在数据库中加密存储(AES-256),密钥由环境变量持有
  2. GET /admin/email-config 返回时隐藏密码:smtp.pass = '********'
  3. 修改密码时才传输明文,存入前加密
🔴 A03 ✅ 已修复 安全 · api-keys 模块

AI API Keys 以明文存储在数据库

API:POST /admin/api-keys · GET /admin/api-keys
源码位置:Financial_Admin/server/routes/admin.js — api-keys 路由(约 L1041-1052)
问题描述:用户提交的 API Key(OpenAI/Claude 等)通过 POST /admin/api-keys 存入数据库时,字段名为 api_key_encrypted,但实际存储的是明文值(非加密)。字段名具有误导性,让人误以为已加密。管理员在前端即可查看完整 API Key 明文。
根因分析:INSERT 语句直接将 req.body.api_key 存入 api_key_encrypted 列,未进行任何加密处理。
影响范围:任何 Admin 账户可获取所有用户的 AI API Key,可能产生大量未经授权的 AI API 消费(费用由用户承担)。
修复内容:
  1. POST /admin/api-keys 存储前使用 AES-256-GCM 加密(格式:AES:<base64(iv:authTag:ciphertext)>
  2. ✅ 解密时自动识别格式(新 AES 格式 vs 旧 Base64 格式,兼容现有数据)
  3. ✅ 加密密钥从环境变量 API_KEY_ENCRYPTION_KEY 读取(64位 hex),非生产环境有 fallback
  4. ✅ AI 代理调用时解密后通过 HTTP header 传递,不落盘不解密到磁盘
  5. ✅ 前端仅显示掩码(********abcd),不暴露明文
🔴 A04 ✅ 已修复 数据安全 · transactions 模块

CSV 导入执行全量 DELETE,数据不可恢复

API:POST /api/transactions/:billId/upload(Analyzer 后端)
问题描述:上传 CSV 文件后,后端执行 先删后插 策略:先 DELETE FROM transactions WHERE bill_id = ?,再批量 INSERT 新数据。旧数据被不可恢复地删除。
影响范围:用户误上传、空文件上传、列错位上传都会导致历史交易数据全部丢失,无法恢复。
修复内容:
  1. DELETE FROM transactions 改为 UPDATE ... SET deleted_at = now()(软删除),旧数据保留在数据库中
  2. ✅ GET /transactions 和 Admin 后台均过滤 deleted_at IS NOT NULL 的记录
  3. ✅ 数据库已支持软删除(deleted_at 列,2026-04-07 迁移添加)
  4. ⚠️ 若需彻底清理,建议通过 Admin 审计日志清理接口定期删除超过 90 天的软删除记录