RFC2617- HTTP Authentication自译本

Save this PDF as:
 WORD  PNG  TXT  JPG

Size: px
Start display at page:

Download "RFC2617- HTTP Authentication自译本"

Transcription

1 RFC2617- HTTP Authentication 自译本 Network Working Group Request for Comments: 2617 Obsoletes: 2069 Category: Standards Track J. Franks Northwestern University P. Hallam-Baker Verisign, Inc. J. Hostetler AbiSource, Inc. S. Lawrence Agranat Systems, Inc. P. Leach Microsoft Corporation A. Luotonen Netscape Communications Corporation L. Stewart Open Market, Inc. June 1999 HTTP Authentication: Basic and Digest Access Authentication 备忘 (Status of this Memo) 本文档跟踪记录 Internet 团体为完善协议而进行的讨论 建议 详情请参见官方文件 (STD1) 本文可任意分发 版权声明 (Copyright Notice) Copyright (C) The Internet Society (1999). All Rights Reserved.

2 摘要 (Abstract) HTTP/1.0 中包括基本访问鉴别方案 (Basic Access Authentication scheme) 该方案不是安全的用户授权方法 ( 除非与其它安全方法联合使用, 如 SSL[5]), 因为其用户名和口令在网络上是以明文方式传送的 本文档还提供了 HTTP 鉴别框架的规范, 有关原始的基本鉴别方案和基于哈希加密的方案的内容, 请参见分类访问鉴别 (Digest Acccess Authentication) 从 RFC2069 公布以来, 其中涉及的一些可选元素因为出现问题而被移出 ; 而还有一些新的元素因为兼容性的原因而被加入, 这些新元素虽然是可选的, 但还是强烈建议使用的, 因而,RFC2069[6] 最终可能会被本规范所替代 与基本方式类似的是, 分类鉴别授权对通讯双方都知道的秘密 ( 如口令 ) 进行校验 ; 而与基本方式不同的是, 该校验方式中的口令不以明文方式传输, 而这正是基本方式的最大弱点 正象其它大多数授权协议那样, 该协议最大的风险不在于其协议本身, 而是它周边的应用程序

3 目录 (Table of Contents) 1 访问鉴别 (Access Authentication) 1.1 对 HTTP/1.1 规范的依赖 (Reliance on the HTTP/1.1 Specification) 1.2 访问鉴别框架 (Access Authentication Framework) 2 基本鉴别方案 (Basic Authentication Scheme) 3 分类访问鉴别方案 (Digest Access Authentication Scheme) 3.1 介绍 (Introduction) 目的 (Purpose) 操作概述 (Overall Operation) 分类值表示 (Representation of digest values) 局限性 (Limitations) 3.2 分类标题规范 (Specification of Digest Headers) WWW- 鉴别回应标题 (The WWW-Authenticate Response Header) 授权请求标题 (The Authorization Request Header) 鉴别信息标题 (The Authentication-Info Header) 3.3 分类操作 (Digest Operation) 3.4 安全协议商议 (Security Protocol Negotiation) 3.5 例子 (Example) 3.6 代理鉴别和代理授权 (Proxy-Authentication and Proxy-Authorization) 4 安全考虑 (Security Considerations) 4.1 使用基本鉴别方式的客户端鉴别 (Authentication of Clients using Basic Authentication) 4.2 使用分类鉴别方式的客户端鉴别 (Authentication of Clients using Digest Authentication) 4.3 使用有限制的 nonce 值 (Limited Use Nonce Values) 4.4 用基本鉴别方式来进行分类比较 (Comparison of Digest with Basic Authentication) 4.5 攻击回放 (Replay Attacks) 4.6 由多方鉴别方案产生的弱点 (Weakness Created by Multiple Authentication Schemes) 4.7 在线字典攻击 (Online dictionary attacks)

4 4.8 中间人 (Man in the Middle) 4.9 选择纯文本攻击 (Chosen plaintext attacks) 4.10 用预先计算的字典攻击 (Precomputed dictionary attacks) 4.11 批方式暴力攻击 (Batch brute force attacks) 4.12 假冒服务器欺骗 (Spoofing by Counterfeit Servers) 4.13 存储口令 (Storing passwords) 4.14 摘要 (Summary) 5 例子实现 (Sample implementation) 6 感谢 (Acknowledgments) 7 参考书目 (Reference) 8 作者地址 (Authors' Addresses) 9 完整版权声明 (Full Copyright Statement)

5 1 授权鉴别 (Access Authentication) 1.1 对 HTTP/1.1 规范的依赖 (Reliance on the HTTP/1.1 Specification) 本规范和 HTTP/1.1 规范 [2] 一起使用, 它使用 HTTP/1.1 文档 2.1 节的补充反馈方式 (Augmented BNF), 并依赖于该文档对非终端 (non-terminals) 的定义及对其它方面的描述 1.2 访问鉴别框架 (Access Authentication Framework) HTTP 提供了简单的挑战 - 回应鉴别机制, 它可能被服务器用来质询客户端请求, 也可能被客户端用来提供鉴别信息 授权方案用可扩展的 大小写敏感的符号来标识, 后跟获取证明所需要的以逗号分隔的 属性 - 值 对 auth-scheme = token auth-param = token "=" ( token quoted-string ) 401( 未授权 ) 回应消息被原始服务器端用来质询用户代理的授权 该回应必须包括含有至少一个被请求资源质询 (challenge) 的 WWW- 鉴别标题域 407( 需要鉴别代理 ) 回应消息被代理用来质询客户端的授权, 它的代理鉴别标题域 (Proxy-Authenticate header field) 必须包括至少一个代理方 (proxy) 对被请求资源的质询 challenge = auth-scheme 1*SP 1#auth-param 注意 : 用户代理 (agent) 解析 WWW- 鉴别 (WWW-Authenticate) 或代理 - 鉴别 (Proxy-Authenticate) 的标题域, 在碰到含有多个质询 () 或多个 WWW- 鉴别标题域时, 要特别小心, 因为这些质询本身可能就包含了以逗号分隔的鉴别参数对 鉴别参数 realm 的定义在所有的鉴别方案中使用 : realm realm-value = "realm" "=" realm-value = quoted-string Realm 指示 ( 大小写敏感 ) 在所有涉及质询 (challenge) 的鉴别方案中都要用到 Realm 值 ( 大小写敏感 ) 要与被访问服务器的 根 URL 的规范用法 ( 即绝对路径为空的服务器的绝对 URI-absoluteURI, 见 节 [2]) 联合使用, 以定义受保护的区间 这些 realm 参数允许将服务器上受保护资源分成若干个区间, 每个区间都有其自己的鉴别方案和 ( 或 ) 授权数据库 Realm 值是字符串, 通常由原始服务器分配, 针对某些鉴别方案可能还有附加的语法问题 注意, 可能存在多个质询 (challenge),auth-scheme 相同, 而 realm 不同的情况 通常, 用户代理 (agent) 在收到 401( 未授权 ) 回应时, 可能 ( 也可能不会 ) 希望服务器对其授权 如果希望授权, 用户代理将在请求中加入授权请求标题 (Authorization request-header) 域 授权域值由信

6 任证书组成, 其中有对用户代理所请求资源领域的授权信息 客户端在收到 407( 需要代理鉴别 ) 回应时, 如希望通过代理进行自身的鉴别, 可在请求中加入代理授权请求标题域 (Proxy-Authorization request-header) 授权域值和代理授权域值都是由信任组成, 这些信任包括被请求资源的客户端鉴别信息 realm 的值 用户代理 (user agent) 必须选用它所能理解的最强的 auth-scheme 及用户回应质询 (challenge) 的请求信任 credentials = auth-scheme #auth-param 注意, 许多浏览器只支持基本方案, 要求它在 auth-scheme 中排在第一位 如果提供最低程度的满意度, 服务器端应只支持基本方案 受保护区间定义了将要自动使用信任的区域 如果早先的请求已经通过认证, 在由授权方案, 参数和 ( 或 ) 用户选择等所指定的时间间隔内, 其它的请求可通过相同的信任来访问该保护区域 除非鉴别方案有特别指定, 否则单个保护区域不能扩展到该服务器以外的范围 如果原始服务器不希望通过发送的请求来接受信任, 它应当返回 401( 未授权 ) 回应 该回应必须包括一个 WWW- 鉴别标题域, 而该域要包含至少一个 ( 可能是新的 ) 对被请求资源的质询 (challenge) 如果代理 (proxy) 不接受用请求方式发送信任, 它应当返回 407( 需要代理鉴别 ) 回应 该回应必须包括一个代理鉴别 (Proxy-Authenticate) 标题域, 而该域要包含至少一个 ( 可能是新的 ), 代理可用的, 对被请求资源的质询 HTTP 协议的访问鉴别并不限于这种简单的质询回应 (challenge-response) 机制, 还可以使用其它的方法, 比如传输级加密或消息封装及通过附加标题域来指定鉴别信息等等 但是, 这些方法不在本文档的讨论范围 代理 (proxy) 必须完全透明地处理原始服务器对用户代理 (user agent) 的鉴别, 也就是说, 它们必须在不做任何改动的前提下将 WWW- 鉴别和授权标题向前推送, 这方面的规定见 [2] 的 14.8 节 代理 - 鉴别 (WWW-Authenticate) 和代理 - 授权 (Proxy-Authorization) 标题域都是 hop-by-hop 标题 ( 见 [2] 的 节 ) 2 基本鉴别方案 (Basic Authentication Scheme) 用户代理必须对于每个领域 (realm) 通过用户标识 (user-id) 及口令来对自身进行授权, 这是基本授权方案的工作模式 Realm 值应当被看作不透明的字符串, 该值将用于同服务器端其它的 realm 值相比较 只有用户标识及口令通过受保护资源的认证, 服务器才会给请求授权 授权参数没有可选项 对于基本方案, 上面所述框架的应用形式如下 : challenge credentials = "Basic" realm = "Basic" basic-credentials 在接收到对受保护区域的未经认证的资源请求时, 服务器应当回应一个质询 (challenge), 如下 : WWW-Authenticate: Basic realm="wallyworld" WallyWorld 是由服务器分配的字符串, 用于对请求 URI 所指定的受保护资源进行标识 代理也应使用使用 Proxy-Authenticate 标题域来回应同样的质询

7 分隔 为了接收授权, 客户端需要在基于 64 位 (base64 [5]) 的证书中发送用户标识及口令, 中间用冒号 : basic-credentials base64-user-pass = base64-user-pass = <base64 [4] encoding of user-pass,except not limited to 76 char/line> user-pass = userid ":" password userid = *<TEXT excluding ":"> password = *TEXT Userids 可能是大小写敏感的 如果用户代理希望发送用户标识 Aladdin 和口令 open sesame, 应当遵循下面的标题域形式 : Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== 客户端应当假定请求 URI 中所涉及的所有其它路径都在由当前质询的基本 realm 值所指定的保护空间中 客户端在未收到服务器的其它质询时, 可能会优先发送对应于该空间资源的授权标题 同样, 当客户向代理 (proxy) 发送请求, 它也可能在未收到代理服务器的其它质询之前, 在代理授权 (Proxy-Authorization) 标题域中还使用原来的 uerid 和 password 详情参见[ 安全考虑 ] 的第 4 节中与基本鉴别相关的内容 3 分类访问鉴别方案 (Digest Access Authentication Scheme) 3.1 介绍 (Introduction) 目的 (Purpose) HTTP/1.0 中包括基本访问鉴别方案 (Basic Access Authentication scheme[1]) 该方案不是安全的用户鉴别方法, 因为其用户名和口令在网络上是以明文方式传送的 本节提供不以明文方式发送口令的方案规范, 参见 分类访问鉴别 分类访问鉴别 (Digest Access Authentication) 方案不是 WWW 安全问题的最终解决方案 该方案不提供消息内容的加密, 其目的只是创建一个简单的鉴别方法, 以弥补基本鉴别方案中存在的大部分严重漏洞 操作概述 (Overall Operation) 和基本访问鉴别相似, 分类方案基于简单的挑战 - 回应范例 分类方案使用 nonce 值来质询 (challenge) 合法的回应包含对用户名 口令 给定 nonce 值 HTTP 方法 请求 URI 的校验和 (checksum, 缺省是 MD5 的校验和 ), 因此, 口令不会以明文方式传送 有些基本方案要求将用户名及口令预先排成一定的格式 ( 不在本文范围 ) 后再行使用 分类值的表示 (Representation of digest values) 可选的标题, 允许服务器指定用来创建校验和或分类的算法 MD5 是缺省的方法, 而且也是本文唯一提 及的算法

8 在本文中,128 位的 MD5 分类由 32 个可打印的 ASCII 码字符表示 128 位分类中的位按其重要性由高到 低转换, 在某个时刻每 4 位可用下面的 ASCII 表示 每 4 位都可用 16 进制字符 abcdef 表示, 也就是说, 二进制 0000 由字符 0 表示 ;0001 由字符 1 表示, 以后如此类推,1111 用 f 表示 局限性 (Limitations) 本文中描述的分类鉴别方案存在许多已知的局限性, 它只是对基本鉴别方案的替代, 除此外, 别无他用 它是基于口令认证的系统, 在服务器端也要面对任何其它口令系统同样存在的问题 本协议并没有为最初用户和服务器间的口令建立提供安全做法 用户和开发者都应注意, 该协议并不象 Kerberos 或任何客户端的私钥方案那样安全 但是, 即便它一无是处, 总还比在 telnet ftp 用的机制好一些, 当然, 也比基本方案安全 3.2 分类标题的规范 (Specification of Digest Headers) 分类访问鉴别方案在概念上与基本方案相似 更改的 WWW- 鉴别标题行和授权标题行的格式在下面给出 另外, 还有个新的标题, 即 Authentication-Info, 也在下面指定 WWW- 鉴别回应标题 (The WWW-Authenticate Response Header) 服务器在收到对受保护对象未经认证的访问请求时, 会回应 401( 未授权 ) 状态码 在分类方案中,WWW- 鉴别标题应遵循如下写法 : Challenge = "Digest" digest-challenge digest-challenge = 1#( realm [ domain ] nonce [ opaque ] [ stale ] [ algorithm ] [ qop-options ] [auth-param] ) domain = "domain" "=" <"> URI ( 1*SP URI ) <"> URI Nonce = absoluteuri abs_path = "nonce" "=" nonce-value nonce-value = quoted-string opaque = "opaque" "=" quoted-string stale = "stale" "=" ( "true" "false" ) algorithm = "algorithm" "=" ( "MD5" "MD5-sess" token ) qop-options = "qop" "=" <"> 1#qop-value <"> qop-value = "auth" "auth-int" token 上面表示值的意思如下 : Realm 显示给用户看的字符串, 这样他们就知道使用哪个用户名和口令了 该字符串应当包括至少一个执行鉴别的主机名和对可能访问用户群体的附加指示 例如 :

9 domain 指在引号中用空格分隔的 URI 列表 ( 见 RFC XURI[7] 中定义的保护区间 ) 如果 URI 是采用绝对路径, 它是相对于被访问服务器 根 的 URL( 见上面 1.2 节 ) 该列表中的绝对 URI 被用来访问另外一个不同的服务器 客户端可发送同样的鉴别信息来访问由此列表确定的 URI 集合 : 任何 URI, 只要其做为前缀出现在列表中, 就可以认为它指向同样的受保护区域 如果表示被忽略或是空值, 客户端应这样理解, 即, 该保护区域由回应服务器的全部 URI 组成 该表示对代理 - 鉴别 (Proxy-Authenticate) 标题没有意义, 因为对它们来说, 受保护区域总是整个代理 (proxy), 如果出现, 也会被忽略 Nonce 服务器端指定的数据字符, 它应在每个 401 回应产生时, 被唯一地创建 建议该字符以 base64 方式或 16 进制方式出现 另外, 该字符在标题行中传递时是在引号内的, 因此允许使用双引号字符 其内容与实现无关, 而其实现的质量取决于良好的选择 例如,nonce 可能以基于 64 位编码来构造, 如下例 : time-stamp H(time-stamp ":" ETag ":" private-key) 如上, 时间戳 (time-stamp) 是由服务器产生的时间值或其它非重复值 ;Etag 是 HTTP 与请求实体相关的 ETag 标题的值 ;private-key 是只有服务器才知道的值 在碰到这种形式的 nonce 时, 服务器在收到客户鉴别标题后, 会对哈希部分进行重新计算, 并在 nonce 值与标题不符或其 time-stamp 值不够新时拒绝该请求 通过这种方式, 服务器端可以限制 nonce 合法的时间范围 Etag 中的内容将防止对资源的更新版本进行重复请求 ( 注意 : 在 nonce 中包括客户端的 IP 地址将向服务器提出要求, 即不要再重用同样客户发出的 nonce 值 实际上, 单个用户发出的请求会穿越多个代理, 这样做可能导致该过程的中断 另外,IP 地址也是可以假冒的 ) 有的实现可能会选择不接受先前先用的 nonce 或先前使用的分类, 以防止回放式攻击 (replay attack) 或者, 实现在回应 POST PUT 请求时, 也可以选择以前的 nonce 或分类 (digest) 和 GET 请求的 time-stamp 更详细的信息, 见本文第 4 节 nonce 是客户端的 opaque Opaque 由服务器指定的字符串, 客户端不能改动它, 如果并发请求的 URI 也指向同一个受保护区间, 则该信息将被加在这些请求的授权标题域中返给服务器 建议采用 base64 或 16 进制的字符串 stale 一个标志, 用来指示客户端先前的请求因其 nonce 值过期而被拒绝 如果 stale 是 TRUE( 大小写敏感 ), 客户端可能希望用新的加密回应重新进行请求, 而不用麻烦用户提供新的用户名和口令 服务器端只有在收到的请求 nonce 值不合法, 而该 nonce 对应的分类 (digest) 是合法的情况下 ( 即客户端知道正确的用户名 / 口令 ), 才能将 stale 置成 TRUE 值 如果 stale 是 FALSE 或其它非 TRUE 值, 或者其 stale 域不存在, 说明用户名 口令非法, 要求输入新的值 algorithm 是个字符串, 用来指示用来产生分类及校验和的算法对 如果该域没指定, 则认为是 MD5 算法 如果该域指定的算法无法理解, 该质询 (challenge) 将被忽略

10 在本文中, 用 KD(secret,data) 来表示分类算法, 其中 data 指数据,secret 表示采用的方法. 如果表示校验和算法时,data 要写成 H(data); 而 unq(x) 表示将带引号字符串的引号去掉 对于 "MD5" 和 "MD5-sess" 算法 : H(data) = MD5(data) 和 KD(secret, data) = H(concat(secret, ":", data)) 也就是说, 分类 (digest) 就是对 secret 与 data 通过冒号连接一起的结果进行 MD5 运算 而 "MD5-sess" 算法则允许其它第三方服务器参与鉴别 具体用法的区别, 参见 节的描述 qop-options 该表示是可选的, 用于 RFC2069[6] 的向后兼容 它应当被与该分类方案版本兼容的任何实现所使用 如果存在, 它是带引号的一个或多个字符组成的字符串, 用来指示服务器支持的保护水平 (quality of protection) 值 auth 值表示鉴别方式 ; auth-int 表示鉴别保护的完整性 ; 见后面为有该项选择的应用程序重新计算回应指示值 不能识别的选项必须被忽略 auth-param 该指示用于未来扩展 任何无法识别的指示都必须被忽略 授权请求标题 (The Authorization Request Header) 客户端想重试发送请求, 并传递对应前面所框架定义的授权标题行, 如下 : Credentials = "Digest" digest-response digest-response = 1#( username realm nonce digest-uri response [ algorithm ] [cnonce] [opaque] [message-qop] [nonce-count] [auth-param] ) username = "username" "=" username-value username-value = quoted-string digest-uri = "uri" "=" digest-uri-value digest-uri-value = request-uri ; As specified by HTTP/1.1 message-qop cnonce cnonce-value nonce-count nc-value response = "qop" "=" qop-value = "cnonce" "=" cnonce-value = nonce-value = "nc" "=" nc-value = 8LHEX = "response" "=" request-digest request-digest = <"> 32LHEX <"> LHEX = "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b"

11 "c" "d" "e" "f" Opaque 域和算法 (algorithm) 域的值必须在被请求实体的 WWW- 鉴别回应标题中给出 Response 是个字符串, 由 32 个经过计算的 16 进制数字组成, 用来证明用户是否知道口令 Username 用户名, 是指定的 realm 项 digest-uri 从请求队列 (Request-Line) 中的请求 URI 得到的 URI; 这里存在副本是因为代理 (proxy) 在传送时允许对请求队列进行修改 Qop 指示客户端对该消息应用的保护等级 (quality of protection) 如果不为空, 其值必须是服务器支持在 WWW- 鉴别标题中采用的几种值之一 这些值会对请求 - 分类 (request-digest) 的计算造成影响 注意, 这是个单独的符号, 而不是象 WWW- 鉴别 (WWW- Authenticate) 那样, 是带引号的可选值列表 该指示是可选项, 这是为了和 RFC2069[6] 所规定的最小实现保持向后的兼容性 但是, 如果服务器端通过在 WWW- 鉴别 (WWW- Authenticate) 标题域中添加 qop 指示, 就表明该服务器支持 qop, 因而, 必须使用该项指示 Cnonce 当 qop 指示发送了 ( 见上面 ), 该指示必须要指定, 而当服务器端没有在 WWW- 鉴别 (WWW- Authenticate) 标题域中添加 qop 指示时, 该指示一定不能指定 cnonce-value 是客户端提供的字符串, 它由客户端和服务器共同使用, 用来避免选择纯文本攻击 提供共同鉴别 提供某些消息的完整性保护 详情见下面的回应分类 (response-digest) 值和请求 - 分类 (request-digest) 值的计算 nonce-count 当 qop 指示发送了 ( 见上面 ), 该指示必须要指定, 而当服务器端没有在 WWW- 鉴别 (WWW- Authenticate) 标题域中添加 qop 指示时, 该指示一定不能指定 nc-value 是 16 进制表示的计数值, 用来统计客户端发送的带 nonce 值的请求 ( 包括当前请求 ) 个数 例如, 在第一个请求回应中给出了 nonce 的值, 客户端发送 nc= ", 其目的是允许服务器通过对此计算副本的维护来检测请求重复 (request replay), 即当同样的 nc-value 出现两次, 说明请求是可回放的 详情见下面请求 - 分类 (request-digest) 值的构建 auth-param 该指示用于未来扩展 任何无法识别的指示都应被忽略 如果指示或其值不正确, 或者需要的指示没有给出, 都会得到 400( 非法请求 ) 回应 如果请求 - 分类 (request-digest) 是非法的, 登录失败将会被记入日志, 因为在某个单独的客户端出现的重复登录失败可能意味着攻击者正试图猜测口令

12 的 前面定义的请求 - 分类 (request-digest) 指示了其编码方式 下面的定义将表明这些值是如何参与计算 请求 - 分类 (Request-Digest) 如果 qop 值是 "auth" 或 "auth-int": request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) ) <"> 如果 qop 指示没有给出 ( 与 RFC2069 保持兼容性 ): request-digest =<"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) <"> A1 及 A2 的定义在下面 A1 如果算法 ("algorithm") 值是 MD5 或没有指定, 则 A1 是 : A1 = unq(username-value) ":" unq(realm-value) ":" passwd 其中 passwd = < user's password > 如果 "algorithm" 值是 "MD5-sess", 则 A1 只要计算一次, 即当客户端发出第一个请求, 并从服务器收到 WWW- 鉴别 (WWW-Authenticate) 质询 (challenge) 时计算 它使用该质询中的服务器的 nonce, 则用来构建 A1 的第一个客户端 nonce 值应为 : A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )":" unq(nonce-value) ":" unq(cnonce-value) 上式为并发请求和回应的鉴别产生一个 会话密钥 (session key), 该密钥对于每个 鉴别会话 (authentication session) 都是不同的, 这样, 就限制了使用任何一个密钥进行哈希处理的次数 ( 注意 : 鉴别会话更深层次的探讨见 3.3 节 ) 因为服务器只需要使用用户信任的哈希值来产生 A1 值, 因而该机制可允许第三方参与鉴别服务, 这样 WEB 服务器就不再需要实际的口令值了 该协议的规范已经超出了本规范的内容范围 A2 如果 qop 值是 auth 或者没给出, 则 A2:

13 A2 = Method ":" digest-uri-value 如果 "qop" 值是 "auth-int", 则 A2: A2 = Method ":" digest-uri-value ":" H(entity-body) 指示值和带引号的字符串 (Directive values and quoted-string) 注意, 许多指示的取值, 如 username-value 等, 被定义成带引号的字符串 (quoted-string) 而实际上, unq 注释则表示在生成字符串 A1 时, 去掉其外部的引号 因而, 如当授权标题包括该域, 如 : username="mufasa", 则表示用户 Mufasa 的口令是 "Circle Of Life", 这样 H(A1) 就可表示成 Of Life), 注意, 在分类字符串中没有引号 注意, 在分类函数 H() 中的字符串中不允许出现空格, 除非空格出现在带引号的字符串内或者用以标记字符串分类的实体主体中 例如, 上面出现的字符串 A1 必须是 Of Life 在冒号的两边都不可以有空格, 但是允许口令单词之间出现空格 (Circle+SP+Of+SP+Life) 同样, 其它由 H() 分类的字符串也不能在用于域间分隔的冒号两边加空格, 除非空格在引号内或被分类的实体主体内 同样要注意的是, 如果应用了完整性保护 (integrity protection), 即 qop=auth-int, 则 H( 实体 - 主体 ) 就是实体主体的哈希值, 而不是消息主体的哈希值, 该值在发送方进行任何传输编码前计算, 之后, 被接收方删除 注意, 它在任何多部分内容 - 类型 (multipart content-type) 中的每个部分都包括多部分的边界和嵌入标题 多样性考虑 (Various considerations) "Method"( 方法 ) 值是指 HTTP 请求的方法, 见 [2] 的 节 "request-uri" 值请求队列中指定的请求 URI, 见 [2] 的 节 可能还会有 * 号, 即绝对 URL("absoluteURL" 或 "abs_path"), 见 [2] 的 节, 但必须与请求 URI 保持一致 特殊情况, 当请求 URI 是绝对 URL 时, 它也必须是绝对 URL 形式 "cnonce-value" 是客户端可选的值, 用来防止纯文本攻击 进行鉴别的服务器必须保证 "uri" 所指向的资源与请求队列中指定的资源相同 ; 如果不同, 服务器应当回应 400( 非法请求 ) 消息 注意, 由于这可能是攻击的前兆, 服务端的实现可能要对此进行日志记录 在该域的请求 URL 中包含重复信息的目的是因为中间的代理服务器可能会更改客户端的请求队列 已经更改的请求 ( 尽管可以恢复原状 ) 将会导致由客户端计算的分类发生变更 开发者应当注意己鉴别的事务是如何与共享缓存进行交互的 HTTP/1.1 协议规定, 如果共享缓存 ( 见 [2] 的 13.7) 收到的请求中所包含的授权标题和回应是由请求中继传来的, 此时一定不能将该回应做为对其它请求的回答, 除非是回应中包括两种缓存 - 控制 (Cache-Control) 指示中的任何一种才可以

14 如果原始服务器回应中指明 "must-revalidate"( 必须重新授权 ) 的缓存控制指示, 缓存虽然可以在回应并发请求时使用该回应的实体, 但必须先从原始服务器处得到认可才行, 认可的方法是用新请求的请求标题到原始服务器处获取授权 同样, 如果原始服务器回应中包括 public 缓存控制指示, 则对任何并发请求都可应用此回应的实体 鉴别信息标题 (The Authentication-Info Header) Authentication-Info 标题被服务器用做通讯区, 以得到回应中鉴别成功的一些信息 AuthenticationInfo = "Authentication-Info" ":" auth-info auth-info = 1#(nextnonce [ message-qop ] [ response-auth ] [ cnonce ] [nonce-count] ) nextnonce = "nextnonce" "=" nonce-value response-auth = "rspauth" "=" response-digest response-digest = <"> *LHEX <"> nextnonce 值是服务器希望客户端在未来鉴别回应中采用的 nonce 值 服务器可能发送带有 nextnonce 域的 Authentication-Info 标题, 以实现一次性或可变的鉴别方式 如果 nextnonce 域有值, 客户端应当在下次请求中使用该值来构建授权标题 如果有 "stale=true" 存在, 客户端的失败将导致服务器端对请求进行重新鉴别 服务器的实现应当小心处理采用这种机制而引发的潜在性能问题 ; 如果每个请求都包括由服务器指定的 必须在下个请求时使用的 nextnonce 值, 那么管道式 (pipelined) 请求不可能实现 要想实现管理式请求, 而且要顾及性能和安全性的平衡, 可在一段有限时间内, 允许使用旧的 nonce 值 使用 nonce-count 可以在不危害管道的前提下, 保留新服务器 nonce 的大多数安全特性 message-qop 表示用于服务器回应的保护等级选项 "auth" 值表示鉴别 ; 而 "auth-int" 值则表示采用完整性保护的鉴别 服务器在回应客户端请求时, 应当采用和 message-qop 相同的值 在 "response-auth" 中的可选回应分类支持相互鉴别, 即服务器可以证实它知道用户的秘密, 也可通过 qop=auth-int 对回应提供有限的完整性保护 "response-digest" 值用于授权标题中的 "request-digest" 值的计算, 除非指定请求的授权标题中包含 "qop=auth" 或没指定, 则 A2 是 : A2 = ":" digest-uri-value 若包含 "qop=auth-int", 则 A2 是 : A2 = ":" digest-uri-value ":" H(entity-body) 其中,"digest-uri-value" 是请求中授权标题所指向的 "uri" 而"cnonce-value" 和 "nc- value" 必须做为客户端请求的回应消息的组成部分 当指定了 "qop=auth" 或 "qop=auth-int" 时,"response-auth" "cnonce" 和"nonce-count" 必须给出 Authentication-Info 标题允许对通过块编码 (chunked transfer-coding) 传输的 HTTP 消息进行追踪

15 3.3 分类操作 (Digest Operation) 在接收授权标题前, 服务器可能要先检查对应用户名 口令的合法性 这时, 服务器必须执行和客户端相同的分类操作 ( 如,MD5), 并将结果与给定请求 - 分类 (request-digest) 相比较 注意,HTTP 服务器只要支持 H(A1), 就不必知道用户的口令明文, 而授权标题的合法性照样可以鉴别 客户端在回应对受保护区间的 WWW- 鉴别 (WWW-Authenticate) 质询时, 启动同该受保护区间之间的鉴别会话 鉴别会话在客户端收到受保护区中的任何服务器发出的 WWW- 鉴别 (WWW-Authenticate) 质询时终止 客户端应当记住与鉴别会话相关的 username password nonce nonce count 及 opaque 值, 从而能构建将来对指定保护区请求的授权标题 授权标题要被优先包括, 这样做会提高服务器效率, 并避免鉴别质询可能发生的额外循环 服务器可能选择接受旧的授权标题信息, 即使其中包含的 nonce 值已过期 同样, 服务器可能回应 401, 其中包括了新的 nonce 值, 这样会引起客户端重试该请求 ; 通过在回应中指定 stale=true, 服务器通知客户端用新的 nonce 来重试请求, 但是不会再要求输入新的用户名及口令 因为客户端在会话期间要把服务器传给它的 opaque 值返回给服务器,opaque 值可用来传递鉴别会话的状态信息 ( 注意, 其实可通过在 nonce 中包括状态的方法来实现, 这样更安全 简单 ) 例如, 服务器要为已经位于其它服务器的鉴别内容负责, 其实现是, 在第一个 401 回应中包括 domain 指示 ( 包括第二个服务器上的 URI) 和 opaque 指示 ( 包括状态信息 ) 客户端会在服务器回应 301 或 302( 重定向, 即, 指向第二个服务器上的 URI) 时重试该请求 客户端会根据重定向信息, 传送授权标题, 包括 <opaque> 数据 在基本方案中, 代理 (proxy) 必须完全透明地处理分类访问鉴别方案 (Digest access authentication scheme.) 他们必须将 WWW-Authenticate Authentication-Info 和 Authorization header 向前推送, 而不做任何修改 如果代理希望在请求推送到服务器之前对客户进行鉴别, 它可以使用代理 - 鉴别 (Proxy-Authenticate) 和代理 - 授权 (Proxy-Authorization) 标题, 见下面 3.6 节 3.4 安全协议讨论 (Security Protocol Negotiation) 对服务器来说, 了解客户端有能力处理的哪种安全方案是很有用的 可能存在这种情况, 服务器端直接获取分类做为其鉴别方法, 而不管客户端是否支持它 如果发生这种情况, 服务器指定的鉴别方案恰好是客户端所不支持的, 客户端应当明确回应失败 3.5 例子 (Example) 下面的例子假定通过 GET 请求来获取服务器上一个带有访问保护的文档 文档的 URI 是 "http://www.nowhere.org/dir/index.html", 客户端和服务器都知道该文档的用户名是 "Mufasa", 口令是 "Circle Of Life"( 三个单词间用一个空格分隔 ) 客户端在第一次请求该文档时, 没有发送授权标题, 于是服务器回应 : HTTP/ Unauthorized WWW-Authenticate: Digest qop="auth,auth-int",

16 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" 客户端可能会在新的请求中提供用户名和口令, 包括下面的授权标题 : Authorization: Digest username="mufasa", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", qop=auth, nc= , cnonce="0a4f113b", response="6629fae49393a c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41" 3.6 代理鉴别和代理授权 (Proxy-Authentication and Proxy-Authorization) 通过使用 Proxy-Authenticate 和 Proxy-Authorization 标题, 分类鉴别方案也可实现在代理 代理间 代理与原始服务器间进行用户鉴别 这些标题是 Proxy-Authenticate 和 Proxy-Authorization 标题的实例 ( 见 HTTP/1.1 规范 [2],10.33 和 节, 其行为的限制描述 ) 代理鉴别事务与已经描述的十分相似 在接收需要鉴别的请求之前, 代理 / 服务器必须发出 407( 需要代理鉴别 ) 回应, 其中包含了 "Proxy-Authenticate" 标题 Proxy-Authenticate 标题中使用的分类质询 (digest-challenge) 与在前面 节中定义的 WWW- Authenticate 标题一样 客户端 / 代理必须重新发出带有代理授权 (Proxy-Authorization) 标题的请求, 其写法见上面的 节的授权标题 在并发回应时, 服务器将发送代理鉴别信息 (Proxy-Authentication-Info), 其写法与 Authentication-Info 标题域是一样的 注意, 原理上, 可要求客户端提供对代理和最终服务器的自身鉴别, 但不能在同一个回应当中

17 4 安全考虑 (Security Considerations) 4.1 用基本鉴别的客户端鉴别 (Authentication of Clients using Basic Authentication) 基本鉴别方案不是安全的用户鉴别方式, 也不会对以明文方式在物理网络中传输的实体进行任何形式的保护 HTTP 允许使用另外的鉴别方案或加密机制来增强基本协议的安全性能 ( 比如, 一次性口令方案 ) 但它还是比在 LDAP[10] POP 及 IMAP( 见 RFC2195[9]) 上用的 CRAM-MD5 要强许多 它将被用来替代薄弱的危机四伏的基本机制 分类鉴别只提供对实际口令的加密保护 请求或回应的所有其它内容都可以被监听 对双向传递的消息, 分类鉴别方案只提供有限的完整性保护 如果使用了 qop=auth-int 机制, 消息中参与计算 WWW- 鉴别和授权标题域中的回应指示值 ( 见上面的 3.2 节 ) 的部分将受到保护 大多数标题域及其值都可被修改, 从而成为中间层攻击的组成部分 分类鉴别方案不足以满足许多安全 HTTP 事务的需要 为此,TLS 或 SHTTP 成为更适合的协议 分类鉴别尤其不能用于许多需要加密保护的事务处理上 尽管如此, 许多功能仍得以保留, 所以分类鉴别仍然可以继续使用 任何现存的对基本鉴别方案的服务都应根据实际情况尽早转到分类鉴别方案上 4.3 受限的 nonce 值使用 (Limited Use Nonce Values) 分类方案使用服务器指定的 nonce 值为种子生成请求 - 分类 (request-digest) 值 ( 见前面 ) 如 节例子所示的 nonce, 服务器可任意构造它, 而它只能用于指定的客户 指定的资源 有限的时间段或使用数量及其它限制 这样做会增强系统保护机制, 比如可以抵抗回放式攻击 (relay attack, 见 4.5) 然而, 应当看到, 选择生成或检测 nonce 的方法也存在些性能问题及资源消耗 例如, 服务器可以通过记录列表检查最近发出的 nonce 是否返回, 并在每个回应的 Authentication-Info 标题域中发送 next-nonce 指示, 从而实现每个 nonce 值只能使用一次 这种措施可以抵挡哪怕是立即方式的回放攻击, 但是用于检查 nonce 值的开销也非常高, 甚至可能造成管道式请求的鉴别失败 ( 假设返回 nonce 过期指示 ) 类似情况, 要合并请求指定的元素, 如资源的 Etag 值, 也将限制对应版本资源中 nonce 的使用, 从而导致管道失败 因而, 此种方案虽然看来有时很有效, 但从性能上看, 对没采用此方案的一方来说, 将是不可接受的 4.4 基本鉴别与分类鉴别的比较 (Comparison of Digest with Basic Authentication) 分类鉴别和基本鉴别都是处于安全体系的薄弱端, 两者比较的意义在于可在必要时用分类鉴别替代基本鉴别 事务在网络协议上传输的最大危胁在于网络监听 这些事务可能包含一些与交易相关的数据库在线访问 基本鉴别方案下, 偷听者可以得到用户的口令, 这样, 他就可以在数据库中做任何操作, 而且更糟糕的是, 该用户使用相同口令保护的一切资源都将受到危胁 与上面相应, 如果采用分类鉴别方案, 偷听者最多只能得到事务的访问权而得不到用户口令 偷听者所得到的信息将允许他进行回放式攻击, 但只能请求同一个文件, 而如果服务器端对 nonce 的选项进行限制的话, 他的请求将不能得逞

18 4.5 回放式攻击 (Replay Attacks) 对简单的 GET 请求而言, 对分类鉴别方案实行回放式攻击是没有什么意义的, 因为偷听者早已通过回放得到了他所能得到的唯一文件 这是因为被请求文件的 URI 已经在客户端请求中进行了分类, 服务器将只传递这个指定文件 而在基本鉴别方案下则不同, 偷听者会得到用户的口令, 从而得到受此口令保护的任何文件 因而, 从某种目的上看, 防范回放式攻击还是必要的 一个好的分类鉴别的实现可以通过多种方式来解决这个问题 服务器产生 nonce 值是与实现无关的, 但是如果它包含了客户 IP 时间戳 资源的 Etag 及私有服务器密钥 ( 如上面所建议的 ), 将增大回放式攻击的难度 攻击方必须必须让服务器相信请求是来自一个错误的 IP 地址, 从而导致服务器将要发送的文件发到偷听者指定的 IP 而不是原来的 IP 地址 攻击方只有在时间戳过期前才有可能取得成功 对 nonce 中的客户 IP 及时间戳进行分类将允许实现程序不必对事务间的状态进行维护 对应用程序而言, 如果无法容忍可能存在的回放式攻击, 可使用时间少于 1 秒的一次性 nonce 值 当然, 这会增加服务器的额外开销, 如, 服务器要记住在 nonce 时间戳 ( 用来进行分类的 ) 过期前, 哪些 nonce 值已经用过了 不过对于回放式攻击, 该方法很有效 实现程序要防范可能存在的 使用 POST 或 PUT 请求进行的回放式攻击 如果服务器没有使用一次性或限制性的 nonce 或 ( 和 ) 用 qop=auth-int 的完整性保护, 攻击方仍然可以通过假冒表格数据 (form data) 或其它消息主体的方式来构造一个带有合法信任信息的请求, 从而成功地实现回放攻击 即使采用完整性保护, 标题域中的大多数元数据仍不在保护范围之内 实践中, 正确产生 nonce, 并对提供的保护进行检查, 可以有效防范利用先前用过的合法信任来进行的回放式攻击 见 由多方鉴别方案产生的弱点 (Weakness Created by Multiple Authentication Schemes) HTTP/1.1 服务器可能在 401( 鉴别 ) 回应消息中返回多个质询 (challenge), 每个质询可以使用不同的 auth-scheme 用户代理(agent) 必须从该质询中选用它所能理解的最强 auth-scheme 及请求信任 注意, 许多浏览器只能识别基本鉴别方案, 而且要求该方案处于 auth-scheme 列表中的第一项 服务器如提供最小支持, 只应包括基本鉴别方案 当服务器提供数种使用 WWW-Authenticate 标题的鉴别方案以供选择时, 其安全性与最弱的鉴别方案没有什么不同 参见 4.8 节使用多种鉴别方案进行精确攻击的讨论内容 4.7 在线字典攻击 (Online dictionary attacks) 如果攻击方可以偷听的话, 他可以用常见的单词列表组成 nonce/ 回应对来测试 该列表比起全部可能在口令中出现的单词要少很多 按列表中每一个口令计算回应, 并可在每次质询 (challenge) 时得到回报 服务器可以采取措施, 不允许用户使用字典中的单词做为口令, 这样可以降低这种攻击的危险 4.8 中间人 (Man in the Middle) 基本鉴别方案和分类鉴别方案都容易受到 中间人 (man in the middle,mitm) 的攻击 例如, 怀有敌意或不安全的代理 (proxy) 可以肯定地说, 它与所有偷听类问题相关 而且, 它还为攻击者提供了一些额外的机会

19 中间人 攻击可能从一组鉴别方案中的薄弱环节入手, 并希望客户端使用了已经暴露的用户信任 ( 如口令 ) 出于这种原因, 客户端应尽可能从提供的选项中选择它所支持的最强鉴别方案 更高级的 中间人 攻击可能会删除提供的鉴别方案选项, 而替换成一个采用基本鉴别方案的质询, 这样用户和原始服务器交互的信任将使用明文来传输 还有个更阴险的方法, 就是采用 免费 代理缓存服务来欺骗用户 当通过鉴别的信任请求被使用时, 用户代理 (agent) 应当考虑其界面显示的尺度, 而且应记着当发现服务器要求高级别而回应是低级别时, 向用户发出报警信息 另一个不错的主意是, 将用户代理配置成分类鉴别方式, 或转接到其它安全站点上 另外, 有敌意的代理 (proxy) 还可以伪装成客户端发出请求, 当然, 和基本鉴别方案相比, 还是有一定难度的 4.9 选择纯文本攻击 (Chosen plaintext attacks) 分类鉴别方案下, 中间人 或有恶意的服务器可以任意选择客户用来计算回应的 nonce 值 这种方式被称为 选择纯文本 攻击 选择已知的 nonce 可以使密码分析更加容易 [8] 实际上, 对采用纯文本进行分类的功能进行单向分析是不可能的 客户端对抗此攻击的对策是, 在配置中要求使用 cnonce 指示 ; 这将允许客户对输入值按照自己的方式而不是攻击者指定的方式进行哈希变换 4.10 预先计算的字典攻击 (Precomputed dictionary attacks) 分类鉴别方案下, 如果攻击方可执行纯文本攻击, 攻击方可事先按照常见单词参照 nonce 的值, 计算出一个字典, 其中包含回应 - 口令 (response, password) 对 这种计算可以在许多机器上并行展开 通过对字典中的口令进行尝试, 攻击方可能获得某个质询的回应 虽然字典中的大多数口令可能没有, 但是总会有几个成功的 攻击方在挑选质询 (challenge), 及计算每个口令回应中所付出的代价, 终将因许多口令的破译而得到回报 一个含有 100 万个口令 / 回应 (password/response) 对的字典可能要占 3.2G 的磁盘空间 客户端防范此攻击的对策也是使用 connce 指示 4.11 批方式暴力攻击 (Batch brute force attacks) 在分类鉴别方案下, 中间人 可以执行纯文本攻击, 并从多个用户处收集对应同一个 nonce 的回应 通过该方法, 攻击方可以得到口令集中的能生成有效 nonce/response 对的全部口令信息 同时, 该方法也减少了完全匹配 nonce/response 对所需要的时间 这种攻击也可在多个机器上同时进行, 甚至单机也可进行快速的口令搜索 已有报告证实,6 个或 6 个以下字符组成的密码可在几个小时内破解 客户端的对抗策略是使用 cnonce 指示 4.12 假冒服务器欺骗 (Spoofing by Counterfeit Servers) 基本鉴别方案容易受到假冒服务器攻击 当用户坚信他正与一台受基本鉴别方案保护的主机相连时, 他也许不会想到, 此时他可能正与怀有敌意的服务器相连 攻击者可截获口令, 并将其储存起来备用, 同时假装返回一个错误 在分类鉴别方案下, 这种攻击实现下来要难一些, 但前提是客户端必须要求使用分类鉴别

20 方案, 或采用上面提到的一些技术来统计 中间人 攻击 另外, 用户使用的鉴别机制也会在发现这种攻击 时给用户提个醒 4.13 存储口令 (Storing passwords) 分类鉴别方案需要鉴别代理 ( 通常是服务器 ) 将与用户名 口令相关的信息存储到一个由给定的 realm 参数指定的口令文件中 通过, 它包含由用户名和 H(A1) 组成的对 H(A1) 是对用户名 realm 口令进行分类的结果 见上面 这种机制的隐患在于, 一旦口令文件被破解, 攻击方就可通过该 realm 得到本服务器上对指定文档的访问权限 和标准 UNIX 口令文件不同, 不需要对服务器的 realm 参数进行解密, 就可以访问与其相关的文件 另一方面, 解密或更确切说是暴力攻击对于获取用户口令是必须的 这就是 realm 做为分类数据的组成部分存储在口令文件中的原因 这意味着即使一个分类鉴别的口令文件被破译, 即用户名和口令己被暴力攻击所破解, 也不会危及其它使用相同用户名及口令的文件 有两个重要结论 : 第一, 如果口令文件中包含未加密的口令, 则此文件必须受到保护 因为访问文件所需要的许可信息都在 realm 中, 它理当受到看管, 事实上这也容易做到 第二, 任何单独用户使用的 realm 参数都应当是独一无二的 尤其是 realm 字符串应当包括进行鉴别操作的主机名 分类鉴别的弱点是客户端无法对服务器进行鉴别 4.14 摘要 (Summary) 从现代密码字来看, 分类鉴别无疑很脆弱 但从一定范围上看, 它在取代基本鉴别方面还是有一定价值的, 它能从一定程度 ( 不是全部 ) 上补救基本鉴别方案的不足 分类鉴别方案的强壮程度取决于其实际的实现方式 特别是, 依赖服务器实现的 nonce 结构更易受到回放式攻击, 对此, 大多数服务器提供的选项还是恰当的, 如由服务器方来承担使用一次性 nonce 或分类的开销来防范可能的回放式攻击 另外, 还可对 nonce 中的信息进行限制, 如限制单一的 IP 地址 单一的 Etag 或限制 nonce 的寿命周期等等, 来满足安全方面的需求 密码安全的极限, 或说底线 (bottom line) 是 : 任何 (*any*) 己实现的应用与密码标准相比都要稍弱一些, 但是任何 (*any*) 己实现的应用都远比基本鉴别方案高级 5 例子实现 (Sample implementation) 下面的代码实现了了计算请求 - 分类 (request-digest) 和回应 - 分类 (response-digest) 的 H(A1) H(A2), 还将提供一个测试程序以用来计算 3.5 节中例子所用到的值 它使用 MD5 实现 (RFC1321) File "digcalc.h": #define HASHLEN 16 typedef char HASH[HASHLEN]; #define HASHHEXLEN 32

21 typedef char HASHHEX[HASHHEXLEN+1]; #define IN #define OUT /* 计算每个 HTTP 分类 H(A1) 值, 即 calculate H(A1) as per HTTP Digest spec*/ void DigestCalcHA1( IN char * pszalg, IN char * pszusername, IN char * pszrealm, IN char * pszpassword, IN char * psznonce, IN char * pszcnonce, // 计算方式 :md5-sess 或 md5 // 用户名 //realm // 口令 //nonce //cnonce OUT HASHHEX SessionKey // 会话密钥 ); /* 计算每个 HTTP 分类的请求 - 分类 / 回应 - 分类值, 即 calculate request-digest/response-digest as per HTTP Digest spec */ void DigestCalcResponse( IN HASHHEX HA1, /* H(A1) */ IN char * psznonce, /*nonce from server( 来自服务器的 nonce)*/ IN char * psznoncecount, /* 8 hex digits( 八个 16 进制数字 )*/ IN char * pszcnonce, /* client nonce( 客户端的 nonce)*/ IN char * pszqop, /* qop-value(qop 值 ): "", "auth", "auth-int" */ IN char * pszmethod, /* method from the request( 请求方法 )*/ IN char * pszdigesturi, /* requested URL( 请求 URL)*/ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ OUT HASHHEX Response /* request-digest or response-digest */ ); File "digcalc.c": #include <global.h> #include <md5.h> #include <string.h>

22 #include "digcalc.h" /* 将二进制转换成 16 进制 */ void CvtHex( IN HASH Bin, OUT HASHHEX Hex) { unsigned short i; unsigned char j; for (i = 0; i < HASHLEN; i++) { j = (Bin[i] >> 4) & 0xf; if (j <= 9) Hex[i*2] = (j + '0'); else Hex[i*2] = (j + 'a' - 10); j = Bin[i] & 0xf; if (j <= 9) Hex[i*2+1] = (j + '0'); else Hex[i*2+1] = (j + 'a' - 10); }; Hex[HASHHEXLEN] = '\0'; }; /* 计算 H(A1),calculate H(A1) as per spec */ void DigestCalcHA1( IN char * pszalg, IN char * pszusername, IN char * pszrealm, IN char * pszpassword, IN char * psznonce, IN char * pszcnonce, OUT HASHHEX SessionKey // 计算方式 :md5-sess 或 md5 // 用户名 //realm // 口令 //nonce //cnonce // 会话密钥 ) { MD5_CTX Md5Ctx;

23 HASH HA1; MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, pszusername, strlen(pszusername)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, pszrealm, strlen(pszrealm)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, pszpassword, strlen(pszpassword)); MD5Final(HA1, &Md5Ctx); if (stricmp(pszalg, "md5-sess") == 0) { MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, HA1, HASHLEN); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, psznonce, strlen(psznonce)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, pszcnonce, strlen(pszcnonce)); MD5Final(HA1, &Md5Ctx); }; CvtHex(HA1, SessionKey); }; /* 计算每个 HTTP 所指定的 request-digest/response-digest */ void DigestCalcResponse( IN HASHHEX HA1, /* H(A1) */ IN char * psznonce, /* nonce from server */ IN char * psznoncecount, /* 8 hex digits */ IN char * pszcnonce, /* client nonce */ IN char * pszqop, /* qop-value: "", "auth", "auth-int" */ IN char * pszmethod, /* method from the request */ IN char * pszdigesturi, /* requested URL */ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */

24 OUT HASHHEX Response /* request-digest or response-digest */ ) { MD5_CTX Md5Ctx; HASH HA2; HASH RespHash; HASHHEX HA2Hex; // calculate H(A2) MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, pszmethod, strlen(pszmethod)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, pszdigesturi, strlen(pszdigesturi)); if (stricmp(pszqop, "auth-int") == 0) { MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, HEntity, HASHHEXLEN); }; MD5Final(HA2, &Md5Ctx); CvtHex(HA2, HA2Hex); // calculate response MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, HA1, HASHHEXLEN); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, psznonce, strlen(psznonce)); MD5Update(&Md5Ctx, ":", 1); if (*pszqop) { MD5Update(&Md5Ctx, psznoncecount, strlen(psznoncecount)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, pszcnonce, strlen(pszcnonce)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, pszqop, strlen(pszqop));

25 MD5Update(&Md5Ctx, ":", 1); }; MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); MD5Final(RespHash, &Md5Ctx); CvtHex(RespHash, Response); }; File "digtest.c": #include <stdio.h> #include "digcalc.h" void main(int argc, char ** argv) { char * psznonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093"; char * pszcnonce = "0a4f113b"; char * pszuser = "Mufasa"; char * pszrealm = char * pszpass = "Circle Of Life"; char * pszalg = "md5"; char sznoncecount[9] = " "; char * pszmethod = "GET"; char * pszqop = "auth"; char * pszuri = "/dir/index.html"; HASHHEX HA1; HASHHEX HA2 = ""; HASHHEX Response; DigestCalcHA1(pszAlg, pszuser, pszrealm, pszpass, psznonce,pszcnonce, HA1); DigestCalcResponse(HA1, psznonce, sznoncecount, pszcnonce, pszqop, pszmethod, pszuri, HA2, Response);

26 printf("response = %s\n", Response); }; 6 感谢 (Acknowledgments) AbiSource 公司的 Eric W. Sink 是本规范修订以前版本的原始作者 通过讨论方式对本规范做出贡献的其它作者有 : Peter J. Churchyard Ned Freed 和 David M. Kristol Jim Gettys 和 Larry Masinter 对本文档做了最后更新 7 参考书目 (References) [1] Berners-Lee, T., Fielding, R. and H. Frystyk, "Hypertext Transfer Protocol -- HTTP/1.0", RFC 1945, May [2] Fielding, R., Gettys, J., Mogul, J., Frysyk, H., Masinter, L., Leach, P. and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, June [3] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April [4] Freed, N. and N. Borenstein. "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies", RFC 2045, November [5] Dierks, T. and C. Allen "The TLS Protocol, Version 1.0", RFC 2246, January [6] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P., Luotonen, A., Sink, E. and L. Stewart, "An Extension to HTTP : Digest Access Authentication", RFC 2069, January [7] Berners Lee, T, Fielding, R. and L. Masinter, "Uniform Resource Identifiers (URI): Generic Syntax", RFC 2396, August 1998.

27 [8] Kaliski, B.,Robshaw, M., "Message Authentication with MD5", CryptoBytes, Sping 1995, RSA Inc, (http://www.rsa.com/rsalabs/pubs/cryptobytes/spring95/md5.htm) [9] Klensin, J., Catoe, R. and P. Krumviede, "IMAP/POP AUTHorize Extension for Simple Challenge/Response", RFC 2195, September [10] Morgan, B., Alvestrand, H., Hodges, J., Wahl, M., "Authentication Methods for LDAP", Work in Progress. 8 作者地址 (Authors' Addresses) John Franks Professor of Mathematics Department of Mathematics Northwestern University Evanston, IL , USA Phillip M. Hallam-Baker Principal Consultant Verisign Inc. 301 Edgewater Place Suite 210 Wakefield MA 01880, USA Jeffery L. Hostetler Software Craftsman AbiSource, Inc. 6 Dunlap Court

28 Savoy, IL Scott D. Lawrence Agranat Systems, Inc. 5 Clocktower Place, Suite 400 Maynard, MA 01754, USA Paul J. Leach Microsoft Corporation 1 Microsoft Way Redmond, WA 98052, USA Ari Luotonen Member of Technical Staff Netscape Communications Corporation 501 East Middlefield Road Mountain View, CA 94043, USA Lawrence C. Stewart Open Market, Inc. 215 First Street Cambridge, MA 02142, USA 9. 完整版权声明 (Full Copyright Statement)

29 Copyright (C) The Internet Society (1999). All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as needed for the purpose of developing Internet standards in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English. The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns. This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 感谢 (Acknowledgement) 由 Internet 社团提供的 RFC 作者基金