第1章 密码迁移概述:为什么需要密码迁移?常见场景

1.1 为什么要做密码迁移?

说白了,密码迁移就是给用户的密码“搬家”——从旧存储格式搬到新存储格式。你可能会问:好端端的搬什么家?我经历过的几个项目让我特别清楚这里面的痛。

我记得有一次负责一个金融系统的安全审计,发现用户密码还在用 MD5 哈希。那真是个定时炸弹。当时数据库已经被拖了一次,好在密码是加盐的,没出大事。但从那以后,团队决定全面迁移到 bcrypt。这就是典型的密码迁移驱动场景——安全漏洞逼迫你升级。

再比如公司合并,两套用户系统要整合。 A 公司用 SHA-256 ,B 公司用 PBKDF2 。用户不能重复注册吧?那就得在后台把两边的密码存储方式统一起来。我习惯把这个过程叫“密码融冰”——慢慢化掉旧哈希,露出来新算法。

归纳起来,密码迁移的驱动力主要来自:

  • 安全威胁演变:旧的哈希算法不再抗 GPU 或 ASIC 攻击。
  • 合规要求:如等保 2.0、PCI-DSS 强制使用强密钥派生函数。
  • 系统升级:框架升级导致密码组件不再兼容。
  • 用户体验改进:支持无密码登录、多因子认证时的密码格式统一。
  • 成本优化:减少维护多套密码验证逻辑的支出。
我个人习惯,在规划迁移前先做一次“密码健康度扫描”,把数据库里还在用弱算法的用户数量摸清楚,才好评估工作量。

1.2 系统升级场景

系统升级是最常见的迁移场景之一。应用版本从 Spring 4 跳到 Spring Boot 3,或者从 CentOS 迁移到 Alpine 容器——底层加密库都可能变了。

我以前遇到过一个坑:旧系统用的是自定义的“base64+salt+ SHA1 ”拼接,新系统要用标准 bcrypt 。直接复制旧哈希肯定不行。我当时的解决方式是设计一个“过渡区”——在用户表加一列 hash_version ,登录时先按旧算法验证,验证通过后立刻用新算法重新哈希并更新数据库。这叫懒迁移(lazy migration)。

升级前后对比:

特性旧系统新系统
哈希算法MD5 + 固定盐bcrypt (cost=10)
盐管理硬编码自动生成并存储
哈希长度32 字节十六进制60 字节
验证性能微秒级毫秒级(可控)
抗暴力破解弱强(GPU 效率低)
注意!迁移过程中不要一次性更新所有用户密码哈希,否则旧密码会立即失效,引发大量投诉。一定要做懒迁移或灰度分批迁移。

1.3 系统合并场景

合并是一团乱麻。我在一个收购项目中见过最典型的例子:主系统用了 Argon2id ,子公司系统还在用明文存储(别笑,真事)。

合并第一个原则:绝不能在合并过程中要求用户改密码。策略是引入一个“认证路由器”:

  1. 读取用户记录里的 password_hash 和 hash_type 字段。
  2. 根据类型选择对应算法验证。
  3. 若验证通过,且哈希类型不是目标算法,就立刻迁移到目标算法并更新记录。
  4. 未来每次登录都会触发一次迁移机会,直到全部用户完成。

这套方案我至今在用,核心是保持向后兼容。你想想看,如果合并后 10 万用户都要重置密码,客服团队会被骂死的。

系统合并的密码迁移,本质是“哈希算法适配器”模式。我在代码里常用一个工厂类,根据 hash_type 动态创建对应的验证器。

1.4 密码策略变更场景

策略变更有时候是“被迫”的——比如等保要求密码长度至少 8 位,而老用户很多是 6 位。这时候强制重置会造成极大体验伤害。

我建议的策略是分三步走:

  • 阶段一(缓冲期):登录时检查密码长度,如果 < 8 位,弹提示“建议更新”,但不阻拦。
  • 阶段二(渐进强制):30 天后,低于 8 位的用户在登录后强制跳转到改密页面。
  • 阶段三(静默迁移):改为新密码时,自然使用新算法存储。旧算法哈希只保留用于验证旧密码,改密后立刻删除。

另外,如果策略变更涉及算法升级(比如加 salt 或换算法),懒迁移同样适用。不过要特别小心那些长期不登录的“僵尸账户”——它们可能在迁移窗口内一直没触发,最后需要批量处理。

我曾经踩过这个坑:一批休眠账户迁移遗漏,半年后用户回来登录,旧哈希已经不再支持,导致认证失败。从那以后,我总会对超过 90 天未登录的账户做一次离线批处理迁移,并发送通知。

1.5 密码迁移核心流程

三种场景看似不同,但落到底层流程是一致的。我用一张图帮你把脉络理清:

密码迁移 系统升级 系统合并 策略变更 算法升级 平台迁移 用户合并 算法兼容 强度提升 轮换策略 核心目标 保证安全 · 无缝迁移 · 最小化用户影响

这张图揭示了密码迁移的底层逻辑:无论哪种场景,最终都要落到具体的实施动作(算法升级、用户合并、强度提升等)。后面的章节我们会逐一深入这些动作的实现细节。

1.6 避坑指南

作为过来人,我总结三个容易翻车的地方:

  • 直接复制旧哈希:哈希算法不同,复制过去验证必失败。必须设计兼容验证逻辑。
  • 遗漏账户锁定状态:迁移时如果误将锁定账户的密码重置,等于帮用户解锁。我曾在生产环境因为这个被运维骂惨了——记得迁移前检查 account_locked 或 disabled 字段。
  • 忘记监控迁移进度:没有 metrics 你就不知道迁移是卡住了还是完成了。我习惯在应用加一个 password_migration_pending 的 Prometheus 指标,实时看剩余待迁移用户数。
我曾经遇到过,迁移脚本里直接把所有用户的 salt 字段清空了,导致旧哈希验证全部失败。那一次差点回滚到备份。从那以后,我每次迁移前都做一次完整数据库备份,并且先灰度 1% 的用户。

1.7 本章要点

密码迁移不是一个“if-else”能解决的问题。它涉及安全、用户体验、系统兼容性三个维度。三种典型场景——系统升级、系统合并、策略变更——背后的核心手段都是懒迁移(Lazy Migration)和过渡验证。

记住:不要批量改密码,不要直接复制哈希,不要忽略休眠账户。把这些原则刻在脑子里,后面的实战内容你就能稳稳接住了。