HTTP Digest Access Authentication Schema
背景
随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗的日子。本文内容并非完全原创,大多是参考其他文章资料整理所得,感谢每位技术人的开源精神。
介绍
HTTP Digest Access Authentication Schema,HTTP 摘要访问认证模式,是 HTTP 1.1 引入的替代 HTTP Basic Access Authentication Schema 的方案,为了解决 HTTP Basic Access Authentication Schema 中存在的安全问题:通过网络传输未加密的用户ID和密码。
HTTP Digest 同 HTTP Basic 一样,基于 challenge-response 认证机制实现身份认证方案,不同之处在于,HTTP Digest 不会直接发送明文密码,而是采用 checksum 检验方式对请求进行验证。
HTTP Digest Access Authentication 中的 challenge 使用一个 nonce
值,一个有效的 response 包含的 checksum 由 用户ID
、密码
、nonce
、HTTP请求方法
和 HTTP请求URI
通过算法生成(默认 MD5),这样就永远不会以明文方式发送密码。
Challenge
服务器发送的 challenge :Digest digest-challenge
,其中 Digest
指明服务器需要的访问认证方式,digest-challenge
格式如下:
realm | [domain] | nonce | [opaque] | [stale] | [algorithm] | [qop-options] | [auth-param]
其中 realm
和 nonce
这两个字段是必须的。字段说明:
realm
:一个标识受保护资源的字符串,指示用户应该使用哪个用户ID和密码进行认证。此字符串至少应该包含执行认证的主机名称,还可以另外指示哪些用户集合可能具有访问权限,如:registered_users@somehost
。domain
: 授权访问的URI列表,每项之间以空格符分隔。nonce
:服务器每次返回401 Unauthorized
时生成的唯一随机数,通常推荐使用 Base64 编码或十六进制数,实际依赖于服务器的具体实现。服务器向客户端发送 challenge 时会附带一个nonce
随机数,客户端返回的 response 中摘要值计算会用到此nonce
值。nonce
的存在增加了破解密码的难度,防范了 中间人 及 恶意服务器 等攻击类型。RFC 2617建议生成随机数的计算公式:nonce=Base64(timestamp MD5(timestamp:ETag:private-key))
。opaque
:服务器指定的一个数字字符串,客户端后续发送的对相同域(realm )的访问请求中,HTTP Headers 的Authorization
应带有此值(保持不变),通常推荐使用 Base64 编码或十六进制数据。stale
:一个标识,表明客户端的上一请求中使用了过期的nonce
值。如果为TRUE
则提示客户端使用新的加密response
值重试请求。只有当服务器收到一个nonce
过期的请求,但该nonce
有一个有效的摘要(表明客户端知道正确的用户ID和密码)时,服务器才应该将stale
设置为TRUE
。algorithm
:一个字符串,指明生成摘要和校验和(checksum)的算法,如果没有设置则默认采用 MD5。qop
:quality of protection,质量保护,包含auth
和auth-int
两种策略,默认auth
,auth-int
增加了报文完整性检测。auth-param
:为未来扩展保留。
Response
客户端发送的 response :Digest digest-response
,其中 Digest
指明客户端遵从的访问认证方式,digest-response
格式如下:
username | realm | nonce | digest-uri | response | [algorithm] | [cnonce] | [opaque] | [message-qop] | [nonce-count] | [auth-param]
字段说明:
username
:用于特定域(realm)认证的用户ID。digest-uri
:请求的相对URI,因为代理在传输过程中可能修改请求,所以此处重复说明。response
:摘要,是由32位十六进制数字组成的字符串,证明客户端知道密码。qop
:同 challenge 中qop
用途一致,必须是服务器发送的 challenge 中指明支持的qop
之一,此值会影响摘要计算。cnonce
:客户端随机数,使得双方都可以查验对方的身份,并对消息的完整性提供一些保护。nc
:nonce
计数器,是一个十六进制的数值,表示同一nonce
下客户端发送出请求的数量,在第一个 response 请求中nc=00000001
,目的是让服务器保持这个计数器的一个副本,以便检测重复的请求。auth-param
:为未来扩展保留。
摘要计算
response 的值由三步计算而成,使用冒号作为分隔符合并多个数值:
-
对用户ID、域(realm )及密码的合并值计算MD5哈希,结果称为
HA1
(安全相关)。-
如果算法是 MD5 ,则
A1=<userid>:<realm>:<password>
-
如果算法是 MD5-sess ,则
A1=MD5(<user>:<realm>:<password>):<nonce>:<cnonce>
HA1 = MD5(A1) = MD5(username:realm:password)
-
-
对 HTTP 方法以及 URI 的摘要的合并值计算 MD5 哈希,如
GET
和/dir/index.html
,结果称为HA2
(报文相关)。A2
表示是与报文自身相关的信息,比如 URL,A2
加入摘要计算的主要目的是有助于防止反复。-
若
qop
为指定或指定为auth
,则A2=<request-method>:<uri-directive-value>
HA2 = MD5(A2) = MD5(method:digestURI)
-
若
qop
指定为auth-int
,则A2=<request-method>:<uri-directive-value>:MD5(<request-entity-body>)
HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody))
-
-
对
HA1
、nonce
、nc
、cnonce
、qop
以及HA2
的合并值计算 MD5 哈希,结果即为客户端提供的 response 值,计算规则:- 若
qop
没有指定,则response = MD5(HA1:nonce:HA2)
- 若
qop
指定为auth
或auth-int
,则response = MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2)
- 若
流程
-
客户端请求访问受保护资源;
-
服务端接收到请求后,在请求头部(HTTP Request Headers)中未找到
Authorization
,返回401 Unauthorized
,返回响应头(HTTP Response Headers)中带有WWW-Authenticate
:WWW-Authenticate: Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"
-
客户端收到服务器响应后,使用
用户ID
、密码
、nonce
、HTTP请求方法
和HTTP请求URI
,通过指定算法(如默认的 MD5 )计算生成一个摘要,将此摘要与认证请求一起发送给服务器进行认证。客户端发送的认证请求中包含 Authorization 头信息,格式如下:Authorization: Digest username="Mufasa", realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", qop=auth, nc=00000001, cnonce="0a4f113b", response="6629fae49393a05397450978507c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41"
其中
response
的计算过程:HA1 = MD5("Mufasa:testrealm@host.com:Circle Of Life") = 939e7578ed9e3c518a452acee763bce9 HA2 = MD5("GET:/dir/index.html") = 39aff3a2bab6126f332b942af96d3366 response = MD5("939e7578ed9e3c518a452acee763bce9:dcd98b7102dd2f0e8b11d0f600bfb0c093:00000001:0a4f113b:auth:39aff3a2bab6126f332b942af96d3366") = 6629fae49393a05397450978507c4ef1
-
服务器收到客户端的认证请求后,使用存储的密码和发送的参数计算摘要,如果与客户端发送的摘要匹配则认证成功。成功认证后,服务器可以返回
Authentication-Info
响应头,格式:Authentication-Info: nextnonce | [message-qop] | [response-auth] | [cnonce] | [nonce-count]
字段说明:
nextnonce
:如果服务器返回nextnonce
,则客户端下次请求的头信息中Authorization
的nonce
需要设置为此值,否则可能导致服务器要求重新认证。message-qop
:服务器应用与响应的qop
,auth
表示认证,auth-int
表示完整性认证保护,应该与对应的客户端请求中qop
值一致。response-auth
:支持双向身份认证,即表明服务器知道用户的密码。cnonce
:同 response 中cnonce
。nonce-count
:同 response 中nc
。
注意:
- 后续客户端可以提交新请求,重复使用服务器密码随机数(
nonce
),服务器仅在每次响应401 Unauthorized
时发送新的nonce
。 - 后续请求中,十六进制请求计数器(
nc
)必须比前一次要大,否则攻击者可以使用同样的认证信息重放已有的请求。 - 服务器应当记住最近生成的密码随机数(
nonce
),也可以为每一个密码随机数分配一个过期时间,如果客户端请求中携带的是过期的密码随机数,则服务器响应401 Unauthorized
,并在digest-challenge
中添加stale=TRUE
,表明客户端应该使用新的密码随机数重发请求。
总结
HTTP Digest 相比于 HTTP Basic 的优势:
- 不发送明文密码;
- 使用随机数
nonce
防止重放攻击。
HTTP Digest 的缺陷:
- 如果摘要数据被攻击者截获,密码可能会被离线破解;
- 不提供消息完整性和机密性保护,最好配合 HTTPS 使用。
参考
[RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication](RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication)