老MES登录改造:弱密码和暴破一起治

业务场景

某电子厂 SMT/组装车间还在用老 MES。操作员、班组长、工程师用工号加密码登录,用来报工、查工单、看工艺参数。安全审计和客户审厂时发现,很多账号还是初始密码,密码规则也简单,同一个工号可以一直试。弱密码容易被猜中,也可能被脚本暴力破解,共享账号冒用也不好查。

涉及系统

老 MES:登录入口、用户表、密码校验、改密页面、锁定策略。

HR/员工主数据系统:给 MES 同步工号、在职状态、初始密码标记。

Redis:存登录失败次数、验证码、临时锁定状态。

短信/邮件/企业消息网关:发验证码。

审计/安全平台:记登录失败、改密、锁定、解锁日志。

AD/LDAP/统一认证:这期没接,后面再考虑。

原始需求

安全部门和 IT 要求老 MES 登录策略整改,满足等保和客户审厂,同时不能大改系统,不能影响产线登录。原来问题:初始密码长期不改,工号后六位、123456 这类容易猜到;密码没有复杂度要求;登录失败不限次数;失败日志分散,异常账号不好找;如果全量强制改密,可能影响开班和夜班。

排查/分析过程

先从 MES 登录日志和用户表查。看到同一工号短时间大量失败,部分账号长期没改初始密码。数据流是 HR 把工号、状态、初始密码标记同步到 MES 用户表;用户登录时 MES 查用户表校验密码;失败只写普通日志,没有计数和锁定;成功后发会话。确定在认证链路加三件事:登录时识别初始密码,强制跳改密;改密时后端校验复杂度,达到强才让改;登录失败累计,超过阈值弹验证码,再错锁定。前端提示可以做,但后端必须校验。

方案设计

考虑过三种。

A 全量强制重置密码。安全高,但影响所有产线人员,容易停线投诉。

B 只在前端加复杂度提示和初始密码提示。成本低,但能绕过接口,效果差。

C 后端渐进加固。登录识别初始密码并跳改密;改密接口强校验;失败计数放 Redis,超过次数要求验证码,再错锁定;管理员可解锁。

最后选 C。改造成本可控,兼容老 MES,能覆盖弱密码和暴力破解,也能灰度上线。

技术实现

接口:

POST /api/login 入参 account、password、captcha;返回 code:0 成功,1001 需改初始密码,1002 需验证码,1003 账号锁定,1004 密码错误。

POST /api/password/change 入参 oldPassword、newPassword、confirmPassword;后端校验强度。

GET /api/captcha 生成图形或短信验证码。

POST /api/account/unlock 管理员解锁。表结构:

bash 复制代码
ALTER TABLE t_user ADD (
  is_init_pwd TINYINT DEFAULT 1,
  pwd_strength VARCHAR(10),
  pwd_update_time DATETIME,
  fail_count INT DEFAULT 0,
  lock_status TINYINT DEFAULT 0,
  lock_time DATETIME,
  last_login_ip VARCHAR(64)
);

CREATE TABLE t_login_fail_log (
  id BIGINT,
  account VARCHAR(64),
  login_ip VARCHAR(64),
  fail_reason VARCHAR(128),
  create_time DATETIME
);

Redis 键:login:fail:{account}、login:captcha:{account}、login:lock:{account}。

伪代码:

bash 复制代码
login(account, password, captcha) {
  user = queryUser(account);
  if (user.lockStatus == 1) return ACCOUNT_LOCKED;
  if (failCount >= 5 && !verifyCaptcha(account, captcha)) return NEED_CAPTCHA;
  if (!verifyPassword(password, user.pwdHash)) {
    failCount = incrFail(account);
    if (failCount >= 5) return NEED_CAPTCHA;
    if (failCount >= 6) lockAccount(account);
    return PASSWORD_ERROR;
  }
  if (user.isInitPwd == 1) return INIT_PWD_NEED_CHANGE;
  clearFail(account);
  return SUCCESS;
}

changePassword(account, oldPwd, newPwd) {
  if (!verifyPassword(oldPwd, user.pwdHash)) return OLD_PWD_ERROR;
  if (!isStrong(newPwd)) return WEAK_PWD;
  if (contains(account, birthday, phone, newPwd)) return WEAK_PWD;
  updatePwd(account, hash(newPwd), isInitPwd = 0, pwdStrength = "STRONG");
  auditLog(account, "CHANGE_PWD");
}

复杂度规则:长度不少于 12 位,大小写字母、数字、特殊字符至少三类,不能包含工号、生日、手机号,强度达到强才允许提交。日志里账号脱敏成 U***,手机号 138****,密码不落日志。

上线效果

上线后,产线登录流程基本没变化,只有初始密码账号被引导改密,影响可控。初始密码未改比例从约 35% 降到 3% 以下。同一账号短时间暴力失败尝试下降约 90%。锁定和验证码触发都能审计、能告警。客户审厂相关登录安全项关闭,安全部门能查异常 IP、异常账号和锁定记录。

踩坑与反思

锁定阈值一开始设得太激进,差点误伤夜班产线账号。后来改成先验证码、再锁定,并保留管理员快速解锁。

初始密码标记依赖 HR 同步,同步延迟会误判。后来加了定时校准和手工修正入口。

短信验证码有延迟,夜班体验差。后来补了图形验证码和企业消息通道。

失败计数放 Redis,要考虑宕机降级和 DB 一致性。后面改成本地缓存加异步落库。

前端复杂度校验只能提示,后端必须强校验。

锁定策略没覆盖共享账号、多设备登录。后面要接 AD/SSO、MFA、密码历史和风险自适应认证。

后续先灰度再全量,提前准备白名单、自助解锁和审计看板,保证能观测、能回滚。

相关推荐
牧瀬クリスだ3 小时前
单例模式终极指南:从入门到防破坏
单例模式·设计模式
博风7 小时前
9、设计模式应用举例:责任链模式:计算阶乘
设计模式·责任链模式
电子IT笔记9 小时前
人走了,记录不能“空白”: MES×HR 离职人员下拉回显实战
设计模式
博风1 天前
8、设计模式应用举例:适配器模式:Iterator接口与Enumeration接口
设计模式·适配器模式
高晶1 天前
ARM 嵌入式 Linux 系统 U-Boot 开发与移植实战M
设计模式·程序员
geovindu1 天前
rust: Chain of Responsibility Pattern
开发语言·后端·设计模式·rust·责任链模式·行为模式
拆房老料1 天前
【第四部分:Agentic 智能体设计模式】15.为什么 Agent 也需要设计模式
人工智能·设计模式·ai·ai-native
geovindu1 天前
rust: Chain of Responsibility Pattern (续)
开发语言·后端·设计模式·rust·责任链模式·行为模式
Lost of 程序猿1 天前
责任链模式实战:审批流里“层层把关“的艺术
后端·设计模式·asp.net·责任链模式