密码的两种死法:Basic 认证与 Digest 认证的宿命与启示

一、先把它揪出来

判断一个认证是 Basic 还是 Digest,方法简单到令人发指——看 WWW-Authenticate 响应头的第一个单词:

  • Basic 开头:WWW-Authenticate: Basic realm="..."——这是 HTTP/1.0 时代就有的老古董。
  • Digest 开头:WWW-Authenticate: Digest realm="...", nonce="...", algorithm="MD5"...——这是 HTTP/1.1 才登场的“改良版”。

这个头是服务器在说:“嘿,你需要证明你是谁。用什么方式?我告诉你了。”

就这么简单。但这两个单词背后,藏着一段关于安全、信任与历史妥协的故事。

二、Basic:把密码写在明信片上

Basic 认证的逻辑直白得令人心疼。

客户端把用户名和密码用冒号拼在一起——username:password——然后做一次 Base64 编码,塞进 Authorization 头里发出去。

注意,Base64 不是加密,是编码。它存在的唯一目的是把那些可能干扰 HTTP 协议解析的特殊字符(比如冒号)处理掉,让二进制数据能安全地放在文本协议里传输。任何截获了这段流量的人,只需要轻轻一点“解码”,用户名和密码就赫然在目。

这就像你把密码写在明信片上寄出去——邮差能看到,中转站能看到,任何经手的人都能看到。

RFC 2617 的起草者当然知道这个问题。他们在文档里直白地写道:Basic 最大的弱点就是在网络上明文发送密码。但 HTTP/1.0 诞生的1990年代,互联网还是个相对单纯的地方,HTTPS 远未普及,大家觉得“有个认证就不错了”。

于是 Basic 就这样被标准化了。它简单、无状态、所有浏览器都支持,这些优点让它在随后几十年里生命力顽强——尽管它从一开始就知道自己有致命缺陷

三、Digest:不送密码,送“指纹”

Digest 认证的诞生,就是为了修补 Basic 那个“明文送密码”的窟窿。

它的核心思想是:密码打死也不上路

流程是这样的:

  1. 客户端请求受保护资源,服务器返回 401,附带一个 nonce(服务器生成的随机数)。
  2. 客户端把用户名、密码、realm(保护域)、nonce、HTTP方法、请求URI等一堆东西揉在一起,用 MD5 算出一个哈希值(“摘要”)。
  3. 客户端只把这个哈希值发过去,密码原文始终留在本地。
  4. 服务端用自己存的密码副本做同样的计算,比对哈希值。

服务器每次给的 nonce 不同,所以每次的摘要也不同。就算黑客抓到了这次请求的摘要,也没法用它去别的地方冒充——因为换一个 nonce,摘要就变了。

RFC 2617 对此的表述很克制:“Digest 和 Basic 一样,验证通信双方知道同一个共享秘密;但与 Basic 不同,这种验证可以在不发送明文密码的情况下完成。”

这确实是进步。密码不再裸奔了。

四、但 Digest 有个更隐蔽的问题

Digest 看似解决了 Basic 的问题,却引入了一个更深的困境。

它要求服务端必须能够拿到密码原文(或等价物)来做哈希比对。

这意味着,服务端不能对存储的密码做单向哈希(比如 bcrypt、scrypt)。它存的是什么?是 HA1 = MD5(username:realm:password) 这个值。虽然比明文好一点,但本质上,这个 HA1 就是密码的“替身”——谁拿到了 HA1,谁就能冒充用户完成 Digest 认证。

Apache 官方文档对此直言不讳:“与基本认证相比,服务器上的密码存储安全性要低得多。”

这形成了一个悖论:Digest 让传输更安全,却让存储更不安全。你把前门装上了防盗锁,后门却敞开了。

更麻烦的是,MD5 本身早已被证明有碰撞漏洞。而 Digest 认证默认且广泛使用的正是 MD5。一个依赖已破解哈希算法的认证方案,其安全性可想而知。

还有降级攻击——中间人可以篡改响应,强迫客户端从 Digest 降级到 Basic,然后轻松截获密码。Digest 精心构建的安全屏障,在一个简单的协议干扰下就土崩瓦解。

OWASP 的评价很直接:Basic 和 Digest 都被认为是弱认证方法

五、历史的玩笑:更安全的那一个,反而死得更快

Digest 比 Basic 安全——至少在传输层上是这样。但诡异的是,Digest 从未真正流行起来,而 Basic 却活到了今天

为什么?

第一个原因:HTTPS 的到来。

HTTPS(TLS)加密了整个通信通道。在加密通道里,Basic 的“明文传输”问题不复存在了——密码在到达客户端之前就已经被 TLS 加密。既然 HTTPS 能解决 Basic 最大的问题,那为什么要用更复杂、有存储缺陷、还依赖 MD5 的 Digest?

第二个原因:实现复杂度。

Digest 的握手流程、nonce 管理、qop(保护质量)、nc(请求计数)等机制,实现起来远比 Basic 复杂。而 Basic 呢?三行代码搞定。开发者的懒惰——或者说对效率的追求——是一种强大的选择力量。

第三个原因:浏览器支持的历史偶然。

1990年代,Netscape 和 IE 是浏览器的两座大山。有工程师回忆:“没人用 Digest 的原因很简单——Netscape 和 IE 不支持。只要这种情况持续,Digest 永远不会被广泛使用。”技术标准的命运,有时就是这么残酷——再好的设计,如果主流实现不跟进,就是废纸。

第四个原因:范式转移。

就在 Digest 还在挣扎的时候,整个认证的范式已经变了。OAuth 2.0 和 Bearer Token 的出现,代表了一种根本性的转变——认证不再与每个请求的密码学绑定,而是引入了一个独立的、代表“持有者”权限的令牌。这个令牌可以过期、可以撤销、可以精细控制权限。

Digest 还没来得及成为主流,就已经被时代甩下了车。

六、更深层的启示:安全不是技术问题,是生态问题

Basic 和 Digest 的故事告诉我们一个经常被忽视的真相:一个安全方案的成功,从来不只取决于它的技术优劣。

Basic 不安全,但它简单、够用、有 HTTPS 兜底,于是活了下来。Digest 更安全,但它复杂、有存储缺陷、生不逢时,于是成了技术史上的一个注脚。

这让我想起一个哲学层面的观察:安全永远是一个系统性问题,而不是一个点状问题。

Basic 的弱点在传输层,HTTPS 补上了。Digest 的弱点在存储层和算法层——密码存储方案的问题、MD5 的问题、降级攻击的问题——这些问题散布在整个系统的不同层面,没有一个单一的补丁能全部覆盖。

而 OAuth 2.0 / Bearer Token 的成功,恰恰在于它跳出了“在每个请求中验证密码”的框架,把认证和授权分离,把密码的暴露面缩减到最小。这不是在修补漏洞,而是在重新定义问题

七、今天,我们该怎么选?

如果在2026年的今天你还面临这个选择,答案已经非常清晰了:

有 HTTPS,用 Basic。没有 HTTPS,别用 Digest——用 HTTPS。

Digest 的设计目标曾经是“在没有加密通道的情况下提供比 Basic 更好的安全性”。但这个目标在今天已经不再成立。MD5 已破、存储有隐患、实现易出错——Apache 官方在多年前就已经建议:“使用基本认证并通过 mod_ssl 加密整个连接是更好的替代方案。”

对于生产环境的 API,更推荐的是 Bearer Token(OAuth 2.0 / JWT)这类现代方案。Basic 只适合内部系统、开发调试、或那些对安全性要求极低的场景。

至于 Digest——如果你在代码里看到它,大概率是在维护一个十年前的老系统。尊重它,理解它,然后尽快迁移走


Basic 和 Digest 的故事,本质上是一个关于技术如何在现实约束中演化的故事。最优雅的方案不一定胜出,最安全的方案不一定被采用,而那个“足够好”的方案——只要生态愿意拥抱它——就能活得最久。

这不是技术的失败,这是技术的社会性。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容