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:真接口验证(含「强口令通过」正向对照臂,防假绿)。
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
const Controller = require('egg').Controller;
|
||||
const svgCaptcha = require('svg-captcha');
|
||||
const VALIDATION_RULES = require('../validate');
|
||||
// 口令强度策略(唯一事实来源):黑名单 + 纯重复/序列 + 账号派生值 + 长度上下限。
|
||||
// ⚠️ 任何新增的口令写入入口都必须调用它,禁止在控制器内自行判断(见 lib/passwordPolicy.js 模块头)。
|
||||
const passwordPolicy = require('../lib/passwordPolicy');
|
||||
|
||||
/**
|
||||
* 认证控制器 - AuthController
|
||||
@@ -206,6 +209,21 @@ class AuthController extends Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
// 口令强度校验(R63 · C1)
|
||||
// 背景:此前仅由 validate 校验「长度 ≥ 6」,`123456` 完全合法 —— R61 测试人员
|
||||
// 实测「浏览器密码管理器预填 123456,直接提交后即成为账号口令」。
|
||||
// 位置(validate 之后 / 短信码之前)的理由:
|
||||
// · validate 只管「必填 + 格式」,强度是业务规则,分层后两者失败文案互不覆盖;
|
||||
// · 纯本地计算、无副作用,可快速失败,不消耗也不依赖 session 中的验证码。
|
||||
// 错误码 2001 = 口令强度不足(activate 段编码;resetPassword 段用 7001)。
|
||||
// ⚠️ 日志只记命中的规则名,绝不记录口令明文或身份证号。
|
||||
const pwdCheck = passwordPolicy.checkPassword(password, { idNumber: id_number, phone });
|
||||
if (!pwdCheck.ok) {
|
||||
ctx.logger.info(`[口令策略] 激活口令被拒:rule=${pwdCheck.rule}`);
|
||||
ctx.body = ctx.helper.error(2001, pwdCheck.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验短信验证码
|
||||
if (!ctx.session.smsCode || smsCode !== ctx.session.smsCode) {
|
||||
ctx.body = ctx.helper.error(2005, '短信验证码错误');
|
||||
@@ -500,6 +518,18 @@ class AuthController extends Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
// 口令强度校验(R63 · C1)—— 与 activate() 共用同一策略(lib/passwordPolicy.js),
|
||||
// ⚠️ 禁止在本处另行实现:一旦「忘记密码」通道放宽,弱口令缺口会从另一条路重新打开
|
||||
// (这正是本模块必须单源的原因)。
|
||||
// 错误码 7001 = 口令强度不足(resetPassword 段编码;activate 段用 2001)。
|
||||
// 本入口请求体无 phone 字段(见 validate.resetPassword),故仅传 idNumber 做派生值比对。
|
||||
const pwdCheck = passwordPolicy.checkPassword(password, { idNumber: id_number });
|
||||
if (!pwdCheck.ok) {
|
||||
ctx.logger.info(`[口令策略] 重置口令被拒:rule=${pwdCheck.rule}`);
|
||||
ctx.body = ctx.helper.error(7001, pwdCheck.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx.session.smsCode || smsCode !== ctx.session.smsCode) {
|
||||
ctx.body = ctx.helper.error(7004, '短信验证码错误');
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user