摘要

  • RFC 2069 用挑战—响应取代 Basic 的明文密码传送,但服务器所需的 H(A1) 是 realm 范围内的密码等价物,泄露后无需恢复明文即可直接冒用。
  • 基础响应只把这项知识与 nonce、HTTP 方法和请求 URI 绑定;它不加密内容,不自动覆盖全部报文字段,也不证明人的意图、应用授权或事务结果。
  • 地址与时间绑定的 nonce 可以无状态地限制适用窗口,却不能证明首次使用;严格抗重放需要服务器在到期前记住已用响应,成本与证据强度由此相连。

先把改进放回 1997 年

RFC 1945 记录的 HTTP/1.0 Basic,会把用户名与密码拼接后用 Base64 表示。Base64 是编码,不是加密;旁观者取得请求头,就能还原可重复使用的密码。RFC 2069 在 1997 年 1 月提出 Digest Access Authentication:服务器给出 nonce,客户端提交由共享密码、挑战和若干请求字段计算的摘要,密码不再以明文穿过网络。

这个进步真实而且狭窄。规范直接称 Digest 为弱访问认证方案,明确它不加密内容、不处理用户与服务器最初如何安全约定密码,也不如 Kerberos 或客户端私钥方案安全。同期的 RFC 2068 给出 HTTP/1.1 背景;两份文档的发表都不是实现、启用或部署结果。

因此,历史结论应当是“被动监听不再直接得到明文密码”,而不是“HTTP 认证已经安全”。后一句把一项局部修复扩写成了整套系统的收据。

公式恰好画出证明的外沿

RFC 2069 唯一具体说明的散列算法是 RFC 1321 定义的 MD5。核心计算可以写成:

A1 = 用户名 : realm : 密码
A2 = HTTP方法 : 请求URI
响应 = KD(H(A1), nonce : H(A2))

一个匹配结果可以支持几项有限陈述:提交者拥有该用户名与 realm 下的有效秘密材料;响应针对服务器给出的 nonce;它点名了某个方法和 URI。服务器还必须核对 Authorization 中重复出现的 uri 确实对应实际服务的资源,因为代理可能改写请求行。算式相同而资源对应错误,仍不是可接受的验证。

公式没有写进的内容,同样重要。基础响应不自动覆盖其他 HTTP 请求头、网络路径、响应体或应用决定;它不证明是谁在键盘前、不证明自由意志、不授予业务权限,也不说明数据库提交了零次、一次还是两次。内容本身仍然是可见的。

RFC 2069 另设了可选 digest 字段,让 POST/PUT 的实体正文与一组选定元数据进入计算,并建议对这些会改变状态的方法使用一次性 nonce/digest,或者干脆只允许 GET。这个可选机制的存在,正说明基础 response 不是“整份 HTTP 报文签名”。

H(A1) 是摘要,也是可执行能力

服务器核验请求时,不必知道明文密码;保存用户名和 H(A1) 就够了。这常被叙述为“服务器只存哈希”。RFC 2069 自己却给出更严格的风险分类:如果 password file 被窃,攻击者无需解密,就能立即访问该文件所对应 realm 的文档;文件必须像含有未加密密码一样保护。

把 H(A1) 称为“密码等价物”需要保留 realm 边界。realm 参与摘要,同一用户名和密码在另一个 realm 通常产生另一个值。泄露一个 verifier 不自动等于取得跨所有服务可用的明文密码。但在它所属的 realm 内,持有者可以计算服务器会接受的响应。这不是无害的哈希残渣,而是一把受范围限制的钥匙。

realm 也不能消除弱密码的离线猜测。攻击者可以围绕已知用户名、realm 与挑战测试候选密码。把算法输出换成不可读字符,只改变秘密的表示与适用范围,不改变必须按可执行后果来管理它。

nonce 能证明尚在窗口,不能独自证明从未用过

RFC 2069 建议 nonce 可包含:

H(客户端IP : 时间戳 : 服务器私钥)

服务器收到请求后可以自行复算,检查该 nonce 是否由自己签发、是否还在时限内、是否像是从指定地址回来。这种自包含设计不必保存每个已发 nonce,适合降低状态成本。

但“合法且未过期”与“第一次出现”不是一回事。只要窗口还开着,同一个 Authorization 就可能再次通过相同的算式。规范认为对普通 GET 的重放通常收益较小,因为 URI 已绑定,监听者也看过返回文档;它同时提醒,带表单数据的 GET 可能触发动作,POST 或 PUT 的重放还可能制造伪造数据或文件。

若应用不能容忍任何重放,服务器必须让响应一次性,并在 nonce 到期前记住哪些 digest 已经用过,第二次出现就拒绝。由此产生存储、检索、过期清理与碰撞处理;多节点系统还要解决各节点状态的一致性。抗重放不是摘要字符串天然携带的属性,而是 nonce 设计与运行状态共同提供的服务。

后继规范没有穿越回 1997 年

RFC 2617 在 1999 年取代 RFC 2069,其 RFC Editor 记录 明确写着 Obsoletes: 2069,也说明旧规范的一些可选元素因问题被移除。它加入强烈建议使用的 qop 路径、客户端 nonce cnonce 与计数 nc。服务器保存自己的计数副本后,看到相同 nc 才能识别重放;qop=auth-int 还把正文摘要纳入 A2。若没有 qop,为兼容旧实现仍走 RFC 2069 公式。

这个兼容分支是历史证据:客户端随机量、nonce 计数和正文完整性是后续补上的控制,不能被倒写成初版方案已经拥有。

RFC 7616 与其 RFC Editor 记录 延续了 2015 年的修订,加入 SHA-256 和 SHA-512/256,并把 MD5 标为不推荐。然而,它仍重复 H(A1) password file 的警告:文件泄露后可直接冒用 realm,必须像明文密码一样保护。它还指出,算法敏捷性无法挽救人类可记忆弱密码,并建议在 HTTPS 等安全通道上使用 Digest。

现代 RFC 9110 的 HTTP 语义有助于保留最后一道界线:认证凭据可以成为授权决定的输入,却不是授权决定本身,更不是业务执行结果。

一条“Digest 验证成功”日志还缺什么

假设系统留下 POST /payment 的成功验证记录。它最多首先说明:某个验证节点接受了由秘密材料、nonce、方法与 URI 形成的关系。调查仍需回答:用了哪个算法与 qop?正文是否覆盖?nonce 是否第一次使用?所有负载均衡节点是否共享重放状态?该用户名当时映射到哪个主体?主体是否有权支付?应用是否提交?是否产生两次副作用?

Lu Heng 的运行代码优先要求把发表的规则与实际执行分开;最小初始规范要求共同层只保留可局部验证的必要规则,其余决定由运行者承担。在这里,摘要公式是共同规则;realm 管理、nonce 时限、抗重放预算与应用授权都是本地控制面。

关于现实层与符号权力的提醒,则阻止“验证成功”四个字越权。一个散列匹配只能替它覆盖的输入作证。人的意图、授权决定、一次执行与实际效果,必须由各自的执行记录作证。

来源