Commit Graph

4 Commits

Author SHA1 Message Date
89b9b2b0da security(config): 移除内置 JWT 密钥明文兜底,收敛到唯一解析入口(R105-D1)
问题(R104 卫生清理 H2 分类的唯二受版本控制命中):
  config.default.js 原为
      const DEFAULT_JWT_SECRET = 'competition_jwt_secret_key_2026_br!cs';
      secret: process.env.JWT_SECRET || DEFAULT_JWT_SECRET
  该常量为**已入库、任何人可读**的明文,且与 scripts/.creds.local.json 的
  jwt_fallback_secret 同值 ⇒ 等价于「JWT 签名密钥公开 ⇒ 任意人可伪造 token
  冒充任意身份」。它与仓库既有纪律(_creds.py / _db.cjs / 本文件:459 对
  DB_PASSWORD 的「缺失即阻断」)不一致 —— 同族问题各自实现必然漂移。

修法:
  · config/config.default.js
      - 删除 DEFAULT_JWT_SECRET 常量与 `||` 兜底,改调唯一解析入口;
      - 生产安全守卫中同步移除 `jwt.secret === DEFAULT_JWT_SECRET` 比对分支
        (不再存在可比的兜底值;缺失时解析期即抛错)。
  · 新增 scripts/_secret.cjs(与 _db.cjs / _creds.py 同一纪律)
      ① env JWT_SECRET → ② scripts/.creds.local.json:jwt_fallback_secret
      → ③ 明确失败(⛔ 刻意不提供明文兜底)

行为等价性:生产经 systemd EnvironmentFile、本地经 loadDeployEnv 从
deploy/.env.production 注入 JWT_SECRET,正常运行路径逐字不变;仅消除了
「密钥缺失时静默使用公开值」这一分支。

验证:
  · 后端真实重启 ⇒ 配置加载成功,生效 jwt.secret 指纹与注入值一致;
  · 端到端判别力(本地 7001 与生产 8023 各一次):新密钥签发 ⇒ 200,
    旧密钥签发 ⇒ 401(auth.js jwt.verify 分支);
  · scripts/verify_no_secrets.py:HIGH 0,判别力自证 17/17;
  · scripts/verify_script_health.py:H1–H4 PASS。
2026-10-12 10:26:03 +08:00
a2e24aba2a feat(auth): 口令强度策略(弱口令黑名单 / 账号派生值 / 长度上限)
背景(R61 实测):浏览器密码管理器在「设置密码」页预填 123456,考生若直接提交,该口令即成为其账号真实口令;而后端此前仅由 validate 校验「长度 >= 6」,弱口令完全合法。R62 已从前端修掉预填,本次补齐后端这道闸门。

- app/lib/passwordPolicy.js + password_blacklist.json:口令策略唯一事实来源。黑名单刻意做成纯 JSON,供 Node 运行时与运维脚本共用,避免两份清单漂移。

- controller/auth.js:activate() 与 resetPassword() 接入策略(错误码 2001 / 7001),位置在 validate 之后、短信校验之前(纯本地计算、无副作用、快速失败)。

- validate/index.js:补 max:20,与前端 el-form 规则对齐(此前只有前端限制长度上限)。

- scripts/_db.cjs:DB 配置解析唯一入口;scan_weak_password_accounts.cjs:存量弱口令只读扫描器;verify_password_policy_api.cjs:真接口验证(含「强口令通过」正向对照臂,防假绿)。
2026-09-30 11:56:35 +08:00
a019df510d chore(security): 处置已入库的本地 DB 口令(P1-P3;P4 改历史不做)
背景(R14 登记的 §16.3):
  git grep -l "74191221" HEAD 曾命中 7 个文件,全为本地 MariaDB root 口令
  (已核实无生产凭据)。本次按登记项执行 P1-P3;P4(git filter-repo 改写
  历史)明确不做。

P1  database/config.json(dev/test 两段明文口令)
  - 加入 .gitignore
  - 新增 database/config.json.example(口令为 CHANGE_ME 占位符)
  - git rm --cached(保留磁盘文件,本地 sequelize-cli 不受影响)
  - 注意:这是对共享仓库的文件移除。push 后其他克隆需
    cp database/config.json.example database/config.json 并填入口令。

P2  五个脚本口令环境变量化(去明文兜底)
  - add_word_cols.js / add_wx_link.js / show_tables.js / seed-carousel.js
    内联 '74191221' 改为 process.env.DB_PASSWORD(另支持 DB_HOST/DB_USER/DB_NAME)
  - scripts/diagnose_upload_download.cjs:process.env.DB_PASS || '74191221'
    改为 process.env.DB_PASS;用法注释中的示例口令改为占位符
  - 有意的行为变更:未注入 DB_PASSWORD 时明确失败,不再静默使用公开口令

P3  config/config.default.js 消除「生产静默回退公开口令」
  - sequelize.password 去掉 || '74191221'
  - redis.client.password 去掉 || '74191221'
  - 生产安全守卫新增 DB_PASSWORD 缺失阻断(与 JWT_SECRET/APP_KEYS 同源同形)
  - deploy/.env.production 本就含 DB_PASSWORD ⇒ 现网零影响

验证(全部实测):
  - git grep 74191221 工作树零命中(HEAD 仍有,属 P4 不做范围)
  - 裸启动(无任何 DB 变量)成功:本地口令由 config.local.js 提供
  - 端到端:health db:ok + admin-login code=0
  - MySQL 侧独立取证:processlist = root @ competition_db
  - 变异测试(改坏 config.local.js 口令)⇒ 报 1045 且用户名仍为 root
    ⇒ 同证 P3 兜底确已移除 + R18 的生产账号过滤仍生效
  - P2 双向验证:无 DB_PASSWORD 失败 / 注入后正常列 27 张表
  - 门禁四连:verify_scoring_e2e 17/0、verify_exam_submit_integrity 46/0、
    verify_big_question --cleanup(清理+窗口还原)、check_no_proxy_guard 5 变异全检出
2026-09-23 16:34:08 +08:00
WorkBuddy
a303cf2d53 chore: 后端首次基线提交
运行必须源码:app/(controller/model/service/middleware/lib/utils/validate/router)、config/、database/(migrations/seeders/sql)、启动脚本、app/public/templates 模板。
已忽略运行非必须:node_modules/、logs/、run/、app/public 运行时上传数据、.env*、构建/缓存产物。
2026-08-17 12:25:42 +08:00