基本认证便捷灵活,但极不安全。用户名和密码都是以明文形式传送的,也没有采取任何措施防止对报文的篡改。安全使用基本认证的唯一方式就是将其与 SSL 配合使用。
摘要认证是另一种 HTTP 认证协议,它与基本认证兼容,但却更为安全。摘要认证试图修复基本认证协议的严重缺陷。具体来说,摘要认证进行了如下改下:
- 永远不会以明文方式在网络上发送密码。
- 可以防止恶意用户捕获并重放认证的握手过程。
- 可以有选择地防止对报文内容的篡改。
- 防范其他几种常见的攻击方式。
摘要认证并不是最安全的协议。摘要认证并不能满足安全 HTTP 事务的很多需求。对这些需求来说,使用 TLS 和 HTTPS 协议更为合适一些。但摘要认证比它要取代的基本认证强大很多。
摘要认证的工作原理
下面来看看摘要认证的工作原理(简化版本):
a) 客户端请求了某个受保护文档。
b) 在客户端能够证明其知道密码从而确认其身份之前,服务器拒绝提供文档。服务器向客户端发起质询,询问用户名和摘要形式的密码。
c) 客户端传递了密码的摘要,证明它是知道密码的。服务器知道所有用户的秘密,因此可以将客户提供的摘要与服务器自己计算得到的摘要进行比较,以验证用户是否知道密码。另一方在不知道密码的情况下,很难伪造出正确的摘要。
d) 服务器将客户端提供的摘要与服务器内部计算出的摘要进行对比。如果匹配,就说明客户端知道密码(或者很幸运地猜中了!)。可以设置摘要函数,使其产生很多数字,让人不可能幸运地猜中摘要。服务器进行了匹配验证之后,会将文档提供给客户端——整个过程都没有在网络上发送密码。
重放攻击
使用摘要就无需以明文形式发送密码了。可以只发送密码的摘要,而且可以确信,没有哪个恶意用户能轻易地从摘要中解码出原始密码。
但是,仅仅隐藏密码并不能避免危险,因为即便不知道密码,别有用心的人也可以截获摘要,并一遍遍地重放给服务器。摘要和密码一样好用。
为防止此类重放攻击的发生,服务器可以向客户端发送了一个称为随机数 (nonce) 的特殊令牌,这个数会经常发生变化(可能是每毫秒,或者是每次认证都变化)。客户端在计算摘要之前要先将这个随机数令牌附加到密码上去。
在密码中加入随机数就会使摘要随着随机数的每一次变化而变化。记录下的密码摘要只对特定的随机数有效,而没有密码的话,攻击者就无法计算出正确的摘要,这样就可以防止重放攻击的发生。
摘要认证要求使用随机数,因为这个小小的重放弱点会使未随机化的摘要认证变得和基本认证一样脆弱。随机数是在 WWW-Authenticate 质询中从服务器传送给客户端。
摘要认证的握手机制
下面是简化的摘要认证三步握手机制:
(1) 服务器计算出一个随机数。
(2) 服务器将这个随机数放在 WWW-Authenticate 质询报文中,与服务器所支持的算法列表一同发往客户端。
(3) 客户端选择一个算法,计算出密码和其他数据的摘要。
(4) 客户端将摘要放在一条 Authorization 报文中发回服务器。如果客户端要对服务器进行认证,可以发送客户端随机数。
(5) 服务器接收摘要、选中的算法以及支撑数据,计算出与客户端相同的摘要。然后服务器将本地生成的摘要与网络传送过来的摘要进行比较,验证是否匹配。如果客户端反过来用客户端随机数对服务器进行质询,就会创建客户端摘要。服务器可以预先将下一个随机数计算出来,提前将其传递给客户端,这样下一次客户端就可以预先发送正确的摘要了。
预授权
普通的认证方式中,事务结束之前,每条请求都要有一次请求/质询的需要,参见下图 (a)。
如果客户端事先知道下一个随机数是什么,就可以取消这个请求/质询循环,这样客户端就可以在服务器发出请求之前,生成正确的 Authorization 首部了。如果客户端能在服务器要求他计算 Authorization 首部之前将其计算出来,就可以预先将 Authorization 首部发送给服务器,而不用进行请求/质询了。下图 (b) 显示了这种方式对性能的影响。
预授权对基本认证来说并不重要(而且很常见)。浏览器通常会维护一些客户端数据库以存储用户名和密码。一旦用户与某站点进行了认证,浏览器通常会为后继对那个 URL 的请求发送正确的 Authorization 首部。
由于摘要认证使用了随机数技术来破坏重放攻击,所以对摘要认证来说,预授权要稍微复杂一些。服务器会产生任意的随机数,所以在客户端收到质询之前,不一定总能判定应该发送什么样的 Authorization 首部。
摘要认证在保留了很多安全特性的同时,还提供了集中预授权方式。这里列出了三种可选的方式,通过这些方式,客户端无效等待新的 WWW-Authenticate 质询,就可以获得正确的随机数:
- 服务器预先在 Authentication-Info 成功首部中发送下一个随机数;
- 服务器允许在一小段时间内使用同一个随机数;
- 客户端和服务器使用同步的、可预测的随机数算法。
预先生成下一个随机数
可以在 Authentication-Info 成功首部中将下一个随机数预先提供给客户端。这个首部是与前一次成功认证的 200 OK 响应一同发送的。
Authentication-Info: nextnonce="<nonce-value>"
有了下一个随机数,客户端就可以预先发布 Authorization 首部了。
尽管这种预授权机制避免了请求/质询循环(加快了事务处理的速度),但实际上它也破坏了对同一台服务器的多条请求进行管道化的功能,因为在发布下一条请求之前,一定要收到下一个随机值才行。而管道化是避免延迟的一项基本技术。所以这样可能会造成很大的性能损失。
受限制的随机数重用机制
另一种方法不是预先生成随机数序列,而是在有限的次数内重用随机数。比如,服务器可能允许将某个随机重用 5 次,或者重用 10 秒。
在这种情况下,客户端可以随意发布带有 Authorization 首部的请求,而且由于随机数是事先知道的,所以还可以对请求进行管道化。随机数过期时,服务器要向客户端发送 401 Unauthorized 质询,并设置 WWW-Authenticate:stale=true 指令:
WWW-Authenticate: Digest realm="<realm-value>", nonce="<nonce-value>", stale=true
重用随机数使得攻击者更容易成功地实行重放攻击。虽然这确实降低了安全性,但重用的随机数的生存周期是可控的(从严格禁止重用到较长时间的重用),所以应该可以在安全和性能间找到平衡。
同步生成随机数
还可以采用时间同步的随机数生成算法,客户端和服务器可根据共享的密钥,生成第三方无法轻易预测的、相同的随机数序列。