摘要

  • RFC 1938 让服务器保存散列链中的上一个校验值;提交值经一次散列后若与它相等,服务器便接受并以提交值覆盖旧状态。
  • 因此,认证成功不是只读比较,而是一次必须有先后次序的状态推进;同一回答不能在竞态中制造两次成功。
  • 口令短语无需经过网络,但该机制不加密后续会话,也不一般性抵御主动攻击;HOTP、TOTP 则采用了不同的移动因子。

成功会消耗正确答案

设想两台终端几乎同时提交同一个有效口令。对于可重复使用的密码,两次比较都可能为真。按照 RFC 1938 的规则,只有先完成状态推进的一次应当成功:第一份回答被接受后,服务器保存的参照值已经改变,第二份回答即使字节完全相同,也不再是同一个意义上的证据。

RFC Editor 的当前记录 显示,这份由 N. Haller 和 C. Metz 编写的建议标准发布于 1996 年 5 月,后来被 RFC 2289 取代。历史价值并不只在“建议标准”或“已废止”的标签,而在“one-time”背后的机械过程。

用户持有秘密口令短语,并使用一个无需保密的 seed。生成器把二者结合,反复施加单向散列函数。若链从 N 开始,第一次交出的值位于最远端 H^N(S),下一次则退回到 H^(N-1)(S)。服务器不用保存秘密短语,只需保存最近的 64 位校验值和序列位置。

收到 x 后,服务器计算 H(x)。结果若等于当前校验值,x 即成立;随后服务器把 x 本身写成新的校验值。关键不是“比较”,而是“替换”。重放 x 时,H(x) 只会回到已经被替换的旧值,无法匹配当前保存的 x。密码并非随时间神秘失效,而是成功登录主动改写了下次判断条件。

这一结构来自 RFC 1760 记录的 Bellcore S/KEY 系统。它处理的是一个具体问题:登录流量可能被被动窃听,而重复使用的密码会让一次观察变成长期通行证。散列链让秘密短语不必穿越网络,也让截获的本次回答在状态推进后失去价值。

六个单词只是输入界面

挑战串把状态公开出来:算法、序列号和 seed,例如 otp-md5 487 dog2。RFC 1938 要求实现 MD5,建议实现 SHA,并允许 MD4;两端必须选用同一种算法。兼容更多算法不等于安全性相加,最弱的可接受选项可能成为系统下限。

64 位值不适合人工准确录入,于是标准词典提供 2,048 个单词,六个词各承载 11 位,共 66 位。多出的两位用于检查输入和解码错误。它能提示抄错,却不是另一层密码学认证;单词的自然语言含义也不会给散列值增加秘密。

服务器必须接受标准六词形式和十六进制形式,并应当接受替代词典的词组形式。因此,大小写处理、词典选择、编码与规范化都会影响互操作。看似排版层面的处理,可能决定验证器最终收到的是否还是同一组 64 位。

加密比较正确,事务仍可能错误

RFC 1938 明确讨论了一种主动竞态:攻击者听到六词回答的大部分内容,猜出剩余部分,并争取在合法用户之前到达。符合规范的服务器必须防护。文档建议同一用户不要同时存在多个认证会话,同时需要超时机制,避免这种防护反过来成为永久拒绝服务。

更一般的故障发生在服务器内部。若两个进程先读到相同旧校验值,各自判断同一 x 有效,然后再分别写入新值,一个本应只能消费一次的证据就产生了两次成功。散列计算没有错,错的是状态变更缺少原子性。比较和推进必须成为一个不可被并发拆开的决定,或由等效的串行规则保护。

序列还会耗尽。序列号不是装饰,而是剩余认证次数的库存表。重新初始化时必须改变 seed 或秘密短语;两者都沿用,会再次生成已可能被观察过的链。若为了重置而明文传送秘密短语,机制最初要保护的东西就暴露了。

规范留下了一个很现实的边角:推荐的初始化更新流程可以先要求一个旧 OTP,再安装新链。序列到 1 时,用户也许能用最后一个回答登录,却没有下一个回答授权重置。运维若等到归零才动作,可用性故障不是发生在协议之外,而是由协议状态直接产生。

被取代不等于一夜消失

RFC 2289 的状态页 将其列为 1998 年的建议标准;RFC 2289 正文 延续递减散列链,同时补充测试向量并强化运行建议。它建议用 IPsec 应对 TCP 连接劫持等威胁,这恰好说明 OTP 认证与会话保护是两件事。前者成功,不会自动给后者加密。

后来标准把“变化”放在别处。HOTP 以共享秘密和递增计数器生成值,验证端可在前瞻窗口内寻找同步点,并在成功后推进计数器。TOTP 则以时间步作为 HOTP 的移动因子,并要求同一时间步内已经成功验证的 OTP 不得再次接受。它们适合比较,却不能据此断言源自 S/KEY;密钥保存、移动方向和重新同步问题都不同。

共同点在于,认证从来不只是“数字是否相等”。验证器还要知道当前状态、可容忍的邻近状态、成功会消费什么,以及多个副本如何达成一致。正确性是密码学与状态治理的交点。

边界也必须守住。RFC 1938 不提供隐私,也不一般性抵御主动攻击或社会工程。一次通过只能说明回答符合某个账户当前配置的状态,不能独自证明用户获准执行某条命令、后续会话受到保密、操作已经完成、访问可以持久维持,或键盘前的人具有某个法定身份。

“一次性”的现实层

Heng Lu 对运行代码优先的论述可用来检验这套设计。规范说“推进一次”,实际实现却要回答:进程崩溃时写入是否落盘?副本是否看见同一顺序?重置会关闭还是重新打开旧的重放路径?运行证据才会暴露承诺是否兑现。

最小初始规范的价值也在这里显现:共同核心无需包办所有软件,只需明确链、挑战、表示、接受条件和状态推进。实现可以因环境而异,但不能把一个可消费答案变成两个成功会话。

用现实层区分法看,“一次性口令”只是符号描述;运行现实包括校验值、原子替换、并发政策、有限序列和受控重置。任何一层发生漂移,口令都可能在计算上正确、在历史顺序上错误,甚至名为“一次”却被接受两次。

RFC 1938 留下的更深结论,不是密码应当更长,而是一次证明能够改变下一次证明所处的世界。认证一旦具有这种性质,成功就不是读取事实,而是一次必须明确归属、严格排序并长期留痕的写入。

来源

  1. RFC Editor — RFC 1938 当前记录
  2. RFC 1938 — 一次性口令系统
  3. RFC 1760 — S/KEY 一次性口令系统
  4. RFC Editor — RFC 2289 当前记录
  5. RFC 2289 — 一次性口令系统
  6. RFC 4226 — HOTP
  7. RFC 6238 — TOTP
  8. Heng Lu — 运行代码优先
  9. Heng Lu — 最小初始规范、本地化未来决策与自愿采用
  10. Heng Lu — 现实层、符号权力与清晰为何令人敌视