中文rfc2617-001

时间:2021年5月8日16:01:25

HTTP Authentication: Basic and Digest Access Authentication

A、摘要

  • “HTTP/1.0”,包括基本访问的规范身份验证方案。这个方案不被认为是安全的用户身份验证的方法(除非与一些外部安全系统(如SSL[5]),作为用户名和密码以明文形式在网络上传递。
  • 本文档还提供了HTTP的规范认证框架,原始的基本认证方案以及一种基于加密散列的方案,称为“摘要”访问认证”。因此,它也打算作为一个替换RFC 2069[6]。
  • 指定的一些可选元素由于问题,RFC 2069已从本规范中删除自出版以来发现的;添加了其他新元素兼容性,这些新元素已经成为可选的,但是强烈推荐。
  • 与基础的类似,摘要访问身份验证对双方都进行验证向一个知道通信共享秘密(密码)的人;与基本的不同,此验证可以在不发送明密,这是基础的最大的弱点。
  • 和其他大多数人一样认证协议,最大的风险来源通常是不是在核心协议本身,而是在政策和程序围绕它的使用。

B、目录

1访问认证 ................................ 3.
1.1依赖HTTP / 1.1规范 ............ 3.
1.2访问认证框架 ................... 3.
2基本身份验证方案 .......................... 5
3消化访问身份验证方案 .................. 6
3.1介绍 ...................................... 6
3.1.1目的 ......................................... 6
3.1.2整体操作 ............................... 6
3.1.3消化值的表示 ................. 7
3.1.4局限性 ..................................... 7
3.2规范的消化头 ................... 7
3.2.1 WWW-Authenticate响应头 ............ 8
3.2.2授权请求头 ................ 11
3.2.3 Authentication-Info头 .................. 15
3.3消化操作 .................................. 17
3.4安全协议谈判 ..................... 18
3.5的例子 ........................................... 18
3.6代理认证代理授权......19
4安全注意事项 .............................. 19
4.1使用Basic进行客户端认证
身份验证 .................................... 19
4.2使用Digest对客户端进行认证
身份验证 .................................... 20.
4.3有限使用现时标志值 .......................... 21
4.4 Digest与Basic Authentication的比较....22
4.5重播攻击 .................................... 22
4.6多重认证产生的弱点
计划 ........................................... 23
4.7在线词典攻击 ......................... 23
4.8的人中间 ................................. 24
4.9选择明文攻击 .......................... 24
4.10预先计算的字典攻击 .................... 25
4.11批蛮力攻击 ......................... 25
4.12由假冒欺骗服务器 ................... 25
4.13存储密码 ................................. 26
4.14总结 ........................................... 26
5样例实现 ................................ 27
6致谢 ...................................... 31
7参考 ........................................... 31
8作者的地址 ................................... 32
9完整的版权声明 ............................. 34

1、访问认证

1.1 对HTTP/1.1规范的依赖

该规范是HTTP/1.1规范[2]的配套版本。它使用该文档的扩充的BNF第2.1节,并依赖于该文件中定义的非终结符和其他方面HTTP / 1.1规范。

1.2 接入认证框架

HTTP提供了一种简单的询问-响应身份验证机制,该机制用于服务器询问客户端请求以及客户端提供身份验证信息。它使用可扩的不区分大小写的 token(标志) 来标确定认证方法。以下的逗号分隔的属性-数值列表,该列表带有达到认证的所需参数,可以通过该方式实现。

auth-scheme    = token
auth-param     = token "=" ( token | quoted-string )
  • 源服务器的401(未授权)响应消息对用户代理的授权提出质疑。这个响应必须包含一个有合适质疑的WWW-Authenticate头字段去请求资源。
  • 407(代理身份验证请求)响应消息被一个代理用在质疑一个客户端的认证,它必须包含一个代理认证(Proxy-Authenticate)头部域,该头部与包含至少一个合适的质疑,用于代理请求资源。
challenge   = auth-scheme 1*SP 1#auth-param
  • 注意:用户代理在解析WWW-时需要特别注意身份验证或代理身份验证报头字段值(如果包含)不止一个质疑,或者如果有多WWW-Authenticate头字段,因为一个质疑的内容可能本身包含以逗号分隔的身份验证参数列表。
  • 为所有身份验证定义身份验证参数域计划:
realm       = "realm" "=" realm-value
realm-value = quoted-string

realm(领域)

  • 所有人都需要realm指令(不区分大小写)发出质疑的身份验证方案,realm value(区分大小写),与规范的根URL (abs_path为空的服务器的absoluteURI;参见5.1.2节([2]),定义保护空间。这些领域允许服务器上受保护的资源被划分成一组保护空间,每个空间都有自己的保护空间认证方案和/或授权数据库。
  • 域的值是一个字符串,通常由源服务器分配,可能有特定于身份验证方案的附加语义。请注意,同一个auth-scheme可能会有多个质疑但是不同的realms。
  • 一个用户代理希望一个一般的源服务器来验证自己,但是并不一定要这么做;在接收到一个401(Unauthorized,即未被认证)的消息后,它可能会发送一个带有Authorization头部域的请求来让服务器验证自己。
  • 同样的,一个客户希望一个一般的代理来验证自己,但是并不一定要这么做;在接收到407(Proxy Authentication Required 即代理验证请求)的时候,它可能会发送一个
    Proxy-Authorization头部域的请求来让代理验证自己。
  • Authorizationfield value 和 the Proxy-Authorization field value都是有包含客户端认证信息的证书组成,资源的 realm 会被请求。
  • 用户代理必须选择使用一个带有最强它所理解的auth-scheme的challenges ,并且要求来自用户的证书要基于这个challenges 。
credentials = auth-scheme #auth-param
  • 注意,许多浏览器只会识别Basic,并且需要这是第一个auth方案。服务器应该只如果最低限度是可以接受的,则包含Basic。
  • 保护空间决定了凭证可以自动应用于哪个域。如果先前的请求已被授权,则该保护空间内的所有其他请求可以在由身份验证方案、参数和/或用户首选项决定的一段时间内重用相同的凭据。除非认证方案另有定义,否则单个保护空间不能扩展到其服务器范围之外。
  • 如果源服务器不希望接受与请求一起发送的凭据,它应该返回401(未授权)响应。响应必须包含一个WWW-Authenticate报头字段,该字段至少包含一个(可能是新的)适用于所请求资源的询问。如果代理不接受与请求一起发送的凭据,它应该返回407 (proxy Authentication Required)。响应必须包含一个proxy - authenticate报头字段,该字段包含适用于请求的代理的(可能是新的)询问
    资源。
  • HTTP协议不限制应用程序使用这种简单的询问-响应机制进行访问身份验证。还可以使用其他机制,例如传输级别的加密或通过消息封装,并使用附加的头字段指定身份验证信息。然而,这些附加机制并没有在本规范中定义。
  • 代理对于源服务器的用户代理身份验证必须完全透明。也就是说,它们必须毫发无损地转发WWW-Authenticate和Authorization头,并遵循[2]第14.8节中的规则。Proxy-Authenticate和Proxy-Authorization报头字段都是逐跳报头(参见[2]的13.5.1节)。

2、基础的认证方案

  • “基本”身份验证方案基于这样的模型:客户机必须使用每个领域的用户id和密码对自己进行身份验证。
  • 域值应该被认为是不透明的字符串,它只能与该服务器上的其他域进行相等性比较。只有当服务器能够验证request - uri保护空间的用户id和密码时,它才会为请求提供服务。没有可选的认证参数。
  • 对于Basic,使用的框架如下:
  challenge   = "Basic" realm
  credentials = "Basic" basic-credentials

控件内的URI的未授权请求保护空间,带有挑战的源服务器的响应可能与以下几点一样:

WWW-Authenticate: Basic realm="WallyWorld"
  • 在哪里“WallyWorld”是由服务器分配来识别的字符串请求uri的保护空间。代理可以用
    使用Proxy-Authenticate报头字段也面临同样的问题。
  • 为了接收授权,客户端发送用户标识和密码,由一个冒号(":")字符分隔,位于base64的[7]中凭据中的编码字符串。
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
  • 用户ID可能是大小写敏感的。
  • 如果用户代理希望发送用户id“Aladdin”和密码"open sesame",它将使用以下报头字段:
 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
  • 客户端应该假设,位于Request-URI路径字段中最后一个符号元素的深度或深度超过该符号元素的所有路径也在由当前挑战的基本域值指定的保护空间内。客户端可能会先发制人地发送相应的Authorization头,请求该空间中的资源,而不会收到来自服务器的另一个询问。
  • 类似地,当客户端向代理发送请求时,它可以重用proxy - authorization报头字段中的用户标识和密码,而不会收到来自代理服务器的另一个询问。有关与基本身份验证相关的安全注意事项,请参见第4节。

3、摘要访问认证方案

3.1 介绍

3.1.1 目的

  • 被称为“HTTP/1.0”的协议包含了基本访问认证方案[1]的规范。该方案不被认为是一种安全的用户身份验证方法,因为用户名和密码是以未加密的形式在网络上传递的。
  • 本节提供了一种不以明文方式发送密码的方案,称为“摘要访问认证”。摘要访问身份验证方案并不是完全满足万维网安全需求的方案。该方案不提供消息内容的加密。其目的只是创建一种访问身份验证方法,以避免基本身份验证的最严重缺陷。

3.1.2 完整的操作

  • 与基本访问身份验证一样,摘要模式基于一个简单的质询-响应范式。用户名、密码和给定值的摘要方案挑战默认值(MD5校验和)
    nonce值、HTTP方法和请求的URI。这样,
    password永远不会在明文中发送。
  • 就像基本方案一样,用户名和密码必须以某种方式预先安排,而不是由本文件处理。

3.1.3 摘要值的表示

  • 一个可选的头允许服务器指定用于创建校验和或摘要的算法。缺省情况下使用MD5算法,本文档中只介绍MD5算法。
  • 在本文档中,128位的MD5摘要表示为32个ASCII可打印字符。128位摘要中的位从最高位转换为最低位,一次4位转换为它们的ASCII表示,如下所示。每个4位都用熟悉的十六进制表示法表示,由字符0123456789abcdef表示。也就是说,二进制0000由字符'0'、0001、'1'来表示,以此类推,直到1111被表示为'f'。

3.1.4 局限性

  • 本文中描述的摘要身份验证方案存在许多已知的限制。它的目的是替代基本身份验证,仅此而已。它是一个基于密码的系统,(在服务器端)会遇到与任何密码系统相同的问题。特别是,本协议中没有规定在用户和服务器之间建立用户密码的初始安全安排。
  • 用户和实现者应该知道,该协议不像Kerberos那么安全,也不像任何客户端私钥方案那么安全。然而,这总比什么都没有好,比通常使用的telnet和ftp好,比基本身份验证好。

3.2 对摘要头部的指定

  • 摘要访问认证方案在概念上类似于基本方案。修改后的WWW-Authenticate标题行和Authorization标题行格式如下所示。
  • 此外,还指定了一个新的头Authentication-Info。如果服务器接收到一个访问保护对象的请求,那么不发送可接受的授权头,服务器用一个“401 Unauthorized”状态码,一个WWW-Authenticate头作为根据上面定义的框架,摘要方案的值是使用如下:
challenge        =  "Digest" digest-challenge

digest-challenge  = 1#( realm | [ domain ] | nonce |
                      [ opaque ] |[ stale ] | [ algorithm ] |
                      [ qop-options ] | [auth-param] )


domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
URI               = absoluteURI | abs_path
nonce             = "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
    显示给用户的字符串,以便用户知道使用哪个用户名和密码。该字符串至少应该包含执行身份验证的主机的名称,并可能另外指示可能具有访问权限的用户集合。例如“registered_users@gotham.news.com”。
  • realm显示给用户的字符串,以便用户知道使用哪个用户名和密码。该字符串至少应该包含执行身份验证的主机的名称,并可能另外指示可能具有访问权限的用户集合
    。这个指令在Proxy-Authenticate头文件中没有意义,其中,保护空间始终是整个代理;如果存在它应该被忽略。
  • nonce服务器指定的数据字符串,每次进行401响应时都应唯一生成。建议该字符串为base64或十六进制数据。具体来说,由于字符串是作为双引号字符串在标题行中传递的,因此不允许使用双引号字符。
    nonce的内容依赖于实现。实现的质量取决于良好的选择。例如,可以将nonce构造为的64基编码:
time-stamp H(time-stamp ":" ETag ":" private-key)

其中,时间戳是服务器生成的时间或其他非重复的值,ETag是与请求实体相关联的HTTP ETag头的值,私有密钥是只有服务器知道的数据。
使用这种形式的nonce,服务器将在接收到客户端身份验证报头后重新计算散列部分,如果与报头中的nonce不匹配,或者时间戳值不够新,则拒绝请求。
这样服务器就可以限制nonce的有效时间。
包含ETag可以防止重放对资源更新版本的请求。
(注意:在nonce中包含客户机的IP地址似乎可以为服务器提供限制nonce重用到最初获得它的同一客户机的能力。
然而,这将破坏代理场,在代理场中,来自单个用户的请求通常要通过不同的代理。
此外,IP地址欺骗也不是那么难。)
实现可能选择不接受以前使用过的
Nonce或以前使用的摘要,以防止
重放攻击。或者,实现可能选择使用一次性
用于POST或PUT请求的nonces或摘要,以及用于GET的时间戳请求。有关所涉及问题的更多细节,请参见此文档第4节。
nonce对客户端是不透明的。

  • opaque
    由服务器指定的数据字符串,客户端应该在相同保护空间的后续请求的Authorization头中不加更改地返回该数据。建议该字符串为base64或十六进制数据。
  • stale
    一个标志,指示先前来自客户端的请求被拒绝,因为nonce值过时了。
    如果stale为TRUE(不区分大小写),客户端可能只希望用新的加密响应重试请求,而不需要再次提示用户输入新的用户名和密码。
    只有当服务器收到一个请求,其中的nonce无效,但该请求有一个有效的nonce摘要时(表明客户端知道正确的用户名/密码),服务器才应该将stale设置为TRUE。
    如果stale为FALSE,或者除了TRUE以外的任何值,或者stale指令不存在,则用户名和/或密码无效,必须获取新的值。
  • algorithm
    一个字符串,表示用于产生摘要和校验和的一对算法。
    如果不存在,则假定为“MD5”。
    如果算法不被理解,就应该忽略挑战(如果有多个挑战,就使用不同的挑战)。
    在本文中,对秘密为“secret”的数据“data”应用摘要算法得到的字符串记为KD(secret, data),对数据“data”应用校验和算法得到的字符串记为H(data)。
    符号unq(X)表示引号外的字符串X的值。
    适用于“MD5”和“MD5-sess”算法
H(data) = MD5(data)

KD(secret, data) = H(concat(secret, ":", data))

即,摘要是由冒号连接的秘密的MD5
与数据连接。“MD5-sess”算法的目的是
允许高效的第三方认证服务器;为使用差异,请参见3.2.2.2。

  • qop-options
    这个指令是可选的,但这样做只是为了向后兼容RFC 2069 [6];所有符合这个版本的Digest方案的实现都应该使用它。如果存在,它是一个带引号的字符串,由一个或多个令牌组成,指示服务器支持的“保护质量”值。
    auth表示认证;auth-int表示完整性保护认证;的响应指令值的计算请参见下面的描述
    这种选择的应用。不可识别的选项必须是
    忽略了。
  • auth-param
    这个指令允许将来的扩展。任何未被认可的
    指令必须被忽略。

3.2.2 (Authorization Request)认证请求头部

预计客户端将重试请求,并传递一个授权标题行,根据上面的框架定义,利用如下。

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      = "qop" "=" qop-value
       cnonce           = "cnonce" "=" cnonce-value
       cnonce-value     = nonce-value
       nonce-count      = "nc" "=" nc-value
       nc-value         = 8LHEX
       response         = "response" "=" request-digest
       request-digest = <"> 32LHEX <">
       LHEX             =  "0" | "1" | "2" | "3" |
                           "4" | "5" | "6" | "7" |
                           "8" | "9" | "a" | "b" |
                           "c" | "d" | "e" | "f"

opaque字段和algorithm字段的值必须是所提供的值在实体的WWW-Authenticate响应头中要求。

  • response
    按照下面定义计算的32个十六进制数字的字符串,这证明了
    用户知道密码
  • username
    指定领域中的用户名。
  • digest-uri
    Request-Line的Request-URI中的URI复制在这里因为代理允许在传输中更改请求行。
  • qop
    指示客户端应用于消息的“保护质量”。
    如果存在,它的值必须是服务器在WWW-Authenticate头文件中表示它支持的备选项之一。
    这些值会影响请求摘要的计算。
    请注意,这是一个单一的令牌,而不是像WWW- Authenticate中那样的一个引用列表。
    为了保持与RFC 2069[6]的最低实现的向后兼容性,该指令是可选的,但是如果服务器通过在WWW-Authenticate报头字段中提供qop指令来表示支持qop,则应该使用该指令。
  • cnonce
    如果发送qop指令(见上文),则必须指定qop指令,如果服务器没有在WWW-Authenticate报头字段中发送qop指令,则必须不指定。
    cnonce-value是客户端提供的不透明的带引号的字符串值,客户端和服务器都使用它来避免选定的明文攻击,提供相互身份验证,并提供一些消息完整性保护。
    请参见下面对响应摘要和请求摘要值计算的描述。
  • nonce-count
    如果发送qop指令(见上文),则必须指定qop指令,如果服务器没有在WWW-Authenticate报头字段中发送qop指令,则必须不指定。
    nc-value是客户端已发送的请求数(包括当前请求)的十六进制计数,该请求中包含nonce值。
    例如,在响应给定nonce值发送的第一个请求中,客户端发送“nc=00000001”。
    这个指令的目的是允许服务器通过维护它自己的这个计数的副本来检测请求的回放-如果相同的nc-value被看到两次,那么这个请求就是一个回放。
    请参见下面对请求摘要值构造的描述。
  • auth-param
    这个指令允许将来的扩展。任何未被认可的
    指令必须被忽略。
  • 如果一个指令或它的值不正确,或者需要的指令丢失,正确的响应是400 Bad Request。
    如果请求摘要无效,则应该记录登录失败,因为来自单个客户机的重复登录失败可能表明攻击者试图猜测密码。
    上面的请求摘要定义指出了其值的编码。
    以下定义显示如何计算该值。

3.2.2.1 请求摘要

  • 如果qop值为auth或auth-int:
request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
                                          ":" nc-value
                                          ":" unq(cnonce-value)
                                          ":" unq(qop-value)
                                          ":" H(A2)
                                  ) <">

如果"qop"指令不存在(此构造是为了兼容RFC 2069)

request-digest  =
                 <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) >
   <">

下面是A1和A2的定义。

3.2.2.2 A1

如果“算法”指令的值是“MD5”或未指定,则
A1是:

A1       = unq(username-value) ":" unq(realm-value) ":" passwd

地方

passwd   = < user's password >

如果“algorithm”指令的值为“MD5-sess”,则A1为
只计算一次-在第一个请求后的客户端
从服务器接收到WWW-Authenticate挑战。它使用服务器nonce从该挑战,和第一个客户端nonce值
构建A1如下:

 A1       = H( unq(username-value) ":" unq(realm-value)
                     ":" passwd )
                     ":" unq(nonce-value) ":" unq(cnonce-value)
  • 这为后续的请求和响应的认证创建了一个“会话密钥”,每个“认证会话”都不同,从而限制了任何一个密钥散列的材料数量。(注意:关于身份验证会话的进一步讨论见3.3节。)
  • 因为服务器只需要使用用户的哈希值为了创建A1值,这个构造可以与第三方认证服务一起使用,这样网络服务器不需要实际的密码值。这样一个协议的规范超出了这个范围
    规范。

3.2.2.3 A2

如果"qop"指令的值是"auth"或未指定,则A2
是:

 A2       = Method ":" digest-uri-value

如果qop值为auth-int,则A2为:

A2       = Method ":" digest-uri-value ":" H(entity-body)

指令值和带引号的字符串

  • 请注意许多指令的值,例如"username-
    Value ",被定义为"引号字符串"。然而,“unq”符号的形式中去掉周围的引号字符串A1。因此,如果Authorization头包含字段
username="Mufasa", realm=myhost@testrealm.com
  • 用户Mufasa的密码是“生命周期”,那么H(A1)将是H(Mufasa:myhost@testrealm.com:生命的循环),不加引号在经过摘要的字符串中。
    摘要对应的字符串中不允许有空格
    函数H()将被应用,除非引号中存在空白
    字符串或其内容构成要的字符串的实体主体
    消化。例如,上面所示的字符串A1必须是
Mufasa:myhost@testrealm.com:Circle Of Life
  • 冒号两边没有空白,只有白色密码值中使用的单词之间的空格。同样,其他被H()消化的字符串上不能有空格分隔字段的冒号的一边,除非有空格在引用的字符串或正在摘要的实体正文中。
  • 还要注意,如果应用了完整性保护(qop=auth-int), H(实体-主体)是实体主体的散列,而不是消息主体——它是在发送方应用任何传输编码之前和接收方删除该编码之后计算的。请注意,这的每个部分包含多部分边界和嵌入的头任何多部分内容类型。

3.2.2.5 各种注意事项

  • “Method”的值是[2]的5.1.1节中指定的HTTP请求方法。"request-uri"值是[2]的5.1.2节中指定的请求行的request-uri。这可能是"*","absoluteURL"或[2]的5.1.2节指定的"abs_path",但它必须与Request-URI一致。特别是,如果Request-URI是一个“absoluteURL”,它必须是一个“absoluteURL”。“cnonce-value”是一个可选的客户端选择值,其目的是挫败选定明文攻击。
  • 认证服务器必须确保“uri”指令指定的资源与Request-Line中指定的资源相同;
    如果不是,服务器应该返回一个400 Bad Request错误。(因为这可能是攻击的症状,服务器实现者可能会考虑记录此类错误。)从这个字段中的请求URL复制信息的目的是为了处理中间代理可能改变客户机的request- line的可能性。这个更改过的(但假定语义等价)请求不会产生与客户机计算的相同的摘要。
  • 实现者应该知道经过身份验证的事务如何与共享缓存交互。
    HTTP/1.1协议规定,当一个共享缓存(参见[2]的13.7节)收到一个包含Authorization头的请求和一个中继请求的响应时,它绝对不能将该响应作为对任何其他请求的响应返回。
    除非两个Cache-Control(参见[2]的14.9节)指令中的一个出现在响应中。
    如果最初的反应包括“must-revalidate”cache - control指令,缓存可以使用实体在回复后续请求的响应,但必须首先重新验证源服务器,使用新请求的请求头允许原始服务器进行身份验证的新请求。或者,如果原始响应包含了“public”Cache-Control指令,则响应实体可能会被返回以回应任何后续请求。

3.2.3 (Authentication-Info)认证信息头部

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报头,作为实现一次性或其他改变nonces的手段。
    如果出现了nextnonce字段,客户端应该在为其下一个请求构造Authorization头时使用它。如果客户端不能这样做,可能会导致服务器请求用“stale=TRUE”重新进行身份验证。
  • 服务器实现应该仔细考虑使用这种机制带来的性能影响;
    如果每个响应都包含一个nextnonce指令,而该指令必须用于服务器接收到的下一个请求,那么流水线请求将不可能实现。
    应该考虑允许在有限的时间内使用旧的nonce值以允许请求管道的性能和安全性的权衡。使用nonce-count可以保留新服务器nonce的大部分安全优势,而不会对管道产生有害影响。
  • message-qop
    指示服务器应用于响应的“保护质量”选项。
    auth表示认证;
    auth-int表示完整性保护认证。
    服务器应该在响应中使用与客户端在相应请求中发送的message- qop指令相同的值。
  • “response-auth”指令中的可选响应摘要支持相互身份验证——服务器证明它知道用户的秘密,并且使用qop=auth-int也提供了有限的响应完整性保护。
    “response-digest”值是根据Authorization头中的“request-digest”计算的,但如果“qop=auth”或没有在请求的Authorization头中指定,则A2为
  A2       = ":" digest-uri-value

如果 "qop=auth-int",那么

  A2       = ":" digest-uri-value ":" H(entity-body)
  • 其中“digest-uri-value”是
    请求中的授权头。“cnonce-value”和“nc- .
    value"必须是此消息所指向的客户端请求的值是响应。"response-auth", "cnonce"和"nonce-count"如果"qop=auth"或"qop=auth-int"是,指令必须存在指定。
  • 在HTTP的尾部允许使用Authentication-Info报头通过分块转移编码传递的信息。

3.3 摘要操作

  • 在接收到Authorization报头后,服务器可以通过查找与提交用户名对应的密码来检查其有效性。
  • 然后,服务器必须执行由客户端执行的相同摘要操作(例如,MD5),并将结果与给定的请求摘要值进行比较。请注意,HTTP服务器实际上并不需要知道用户的明文密码。
    只要H(A1)对服务器可用,就可以验证Authorization头的有效性。
  • 客户端对保护空间的WWW-Authenticate挑战的响应将启动一个使用该保护空间的身份验证会话。身份验证会话将持续到客户端从保护空间中的任何服务器接收到另一个WWW-Authenticate挑战为止。
  • 客户端应该记住用户名、密码、nonce、nonce计数和与身份验证会话关联的不透明值,以便在该保护空间内的未来请求中构造Authorization头。可以先包含Authorization头;这样做可以提高服务器效率,并避免额外的身份验证挑战往返。
  • 服务器可能选择接受旧的Authorization头信息,即使包含的nonce值可能不是新的。
    或者,服务器可能返回401响应,并返回一个新的nonce值,导致客户端重试请求;
    通过在此响应中指定stale=TRUE,服务器告诉客户端使用新的nonce重试,但不提示输入新的用户名和密码。
  • 因为客户端需要在会话期间返回由服务器提供给它的不透明指令的值,所以不透明数据可以用来传输身份验证会话状态信息。
    (请注意,任何此类使用也可以通过在nonce中包含状态更容易和安全地完成。)例如,一个服务器可能负责验证实际上位于另一个服务器上的内容。它可以通过让第一个401响应包含一个域指令(其值包含第二个服务器上的URI)和一个不透明指令(其值包含状态信息)来实现这一点。
  • 客户端将在服务器可能响应301/302重定向的时间,指向第二台服务器上的URI。客户将遵循重定向,并传递一个Authorization头,包括& lt; opaque>数据。
  • 与基本方案一样,代理必须是完全透明的
    Digest访问认证方案。也就是说,他们必须前进WWW-Authenticate, Authentication-Info和Authorization头都没动。如果代理想在请求之前验证客户端被转发到服务器时,可以使用代理3.6节中描述的身份验证和代理授权头在下面。

3.4 安全协议通过

  • 对于服务器来说,能够知道客户机能够处理哪些安全方案是很有用的。服务器可能希望将Digest作为其身份验证方法,即使服务器不知道客户机支持它。
  • 如果服务器只指定了它不能处理的身份验证方案,则鼓励客户机促进失败。

3.5 例子

  • 下面的示例假设服务器通过GET请求请求一个受访问保护的文档。文档的URI是“http://www.nowhere.org/dir/index.html”。客户端和服务器都知道这个文档的用户名是“Mufasa”,密码是“Circle Of Life”(三个单词之间各有一个空格)。
  • 客户端第一次请求文档时,没有发送Authorization头,因此服务器使用Authorization头进行响应
 HTTP/1.1 401 Unauthorized
         WWW-Authenticate: Digest
                 realm="testrealm@host.com",
                 qop="auth,auth-int",
                 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                 opaque="5ccc069c403ebaf9f0171e9517f40e41"
  • 客户端可以提示用户输入用户名和密码它将用一个新请求响应哪个请求,包括以下内容授权头:
Authorization: Digest username="Mufasa",
                 realm="testrealm@host.com",
                 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                 uri="/dir/index.html",
                 qop=auth,
                 nc=00000001,
                 cnonce="0a4f113b",
                 response="6629fae49393a05397450978507c4ef1",
                 opaque="5ccc069c403ebaf9f0171e9517f40e41"

3.6 Proxy-Authentication(证明) 和 Proxy-Authorization(授权)

  • 通过使用代理认证头和代理授权头,摘要认证方案还可以用于向代理认证用户、向代理认证代理或向源服务器认证代理。
  • 这些头是HTTP/1.1规范[2]的10.33节和10.34节中指定的代理认证头和代理授权头的实例,它们的行为受那里描述的限制。
    用于代理身份验证的事务与前面描述的事务非常相似。
  • 在接收到需要身份验证的请求时,代理/服务器必须发出带有“proxy - authenticate”头的“407 proxy authentication Required”响应。Proxy-Authenticate头中使用的摘要质询与上面3.2.1节中定义的WWW- Authenticate头相同。然后客户端/代理必须重新发出带有代理授权头的请求,使用上面3.2.2节中为授权头指定的指令。
  • 在随后的响应中,服务器发送与Authentication-Info报头字段相同的指令Proxy-Authentication-Info。
  • 请注意,原则上可以要求客户端同时向代理和终端服务器进行身份验证,但决不会在相同的响应中进行。

安全注意事项

4.1使用基本身份验证的客户端身份验证

  • 基本身份验证方案不是一种安全的用户身份验证方法,也不能以任何方式保护实体,实体在作为载体的物理网络中以明文形式传输。HTTP不阻止使用其他身份验证方案和加密机制来提高安全性,也不阻止为基本身份验证添加增强功能(例如使用一次性密码的方案)。
  • 基本身份验证中最严重的缺陷是,它导致用户密码在物理网络上以明文方式传输。
    摘要身份验证试图解决的正是这个问题。
    因为基本身份验证涉及密码的明文传输,所以不应该(没有增强)使用它来保护敏感或有价值的信息。
  • 基本身份验证的一个常见用途是用于识别目的——例如,为了收集服务器上准确的使用统计信息,要求用户提供用户名和密码作为识别手段。
  • 当以这种方式使用时,人们很容易认为,如果非法获取受保护的文件不是一个主要问题,使用这种方法就没有危险。
    只有当服务器同时向用户发出用户名和密码,特别是不允许用户选择自己的密码时,这才正确。
  • 之所以会出现这种危险,是因为无知的用户经常重用单个密码,以避免维护多个密码。如果服务器允许用户选择自己的密码,那么威胁不仅是对服务器上文档的未经授权访问,还包括对用户使用相同密码保护的其他系统上任何其他资源的未经授权访问。
    此外,在服务器的密码数据库中,许多密码也可能是其他网站的用户密码。
    因此,如果不以安全的方式维护这些信息,系统的所有者或管理员可能会使系统的所有用户面临未经授权访问所有这些站点的风险。
  • 基本身份验证也容易受到假冒服务器的欺骗。如果用户认为他连接的主机包含由Basic身份验证保护的信息,而实际上他连接的是一个敌对的服务器或网关,那么攻击者就可以请求密码,存储它以供以后使用,并伪造一个错误。这种类型的攻击对于摘要身份验证是不可能的。服务器实现者应该防范这种由网关或CGI脚本伪造的可能性。特别是,服务器简单地将连接转移到网关是非常危险的。然后,该网关可以使用持久连接机制与客户机进行多个事务处理,同时以客户机无法检测到的方式模拟原始服务器。
  • Nonce数值的使用限制
  • 摘要方案使用服务器指定的nonce来生成请求摘要值(如上面的3.2.2.1节所述)。如3.2.1节中的nonce示例所示,服务器可以自由地构造nonce,使其只能用于特定的客户端、特定的资源、有限的时间或使用次数,或任何其他限制。
  • 这样做可以增强针对重放攻击(参见4.5)等攻击提供的保护。但是,应该注意的是,为生成和检查nonce而选择的方法还具有性能和资源方面的含义。例如,服务器可以选择允许每个nonce值只使用一次,方法是维护一个记录,记录每个最近发出的nonce是否已经被返回,并在每个响应的Authentication-Info报头字段中发送一个next-nonce指令。
  • 这甚至可以防止立即重放攻击,但是检查nonce值的成本很高,而且可能更重要的是将导致任何流水线请求的身份验证失败(可能返回过时的nonce指示)。
  • 类似地,合并特定于请求的元素(如资源的Etag值)会限制对该资源版本的nonce的使用,也会破坏管道。因此,这样做可能对有副作用的方法有用,但对没有副作用的方法有不可接受的性能。

4.4 摘要与基本认证的比较

  • 摘要身份验证和基本身份验证都处于安全强度谱的薄弱端。但两者之间的比较指出了用Digest代替Basic的效用,甚至是必要性。
    对使用这些协议的事务类型的最大威胁是网络窥探。
  • 例如,这类交易可能涉及对数据库的在线访问,而该数据库的使用仅限于付费用户。
    使用基本身份验证,窃听者可以获得用户的密码。这不仅允许他访问数据库中的任何内容,更糟糕的是,还将允许访问用户使用相同密码保护的其他内容。相反,使用摘要身份验证,窃听者只能访问有问题的事务,而不能访问用户的密码。窃听者获得的信息将允许重放攻击,但只允许请求相同的文档,甚至这也可能受到服务器选择nonce的限制。

4.5 重复攻击

  • 对于简单的GET请求,对Digest身份验证的重放攻击通常是毫无意义的,因为窃听者可能已经看到了通过重放获得的惟一文档。
    这是因为被请求文档的URI在客户机请求中进行了摘要,而服务器只会交付该文档。
    相比之下,在基本身份验证中,一旦窃听者拥有用户的密码,任何受该密码保护的文档都对他开放。
  • 因此,出于某些目的,有必要防止重放攻击。一个好的Digest实现可以通过多种方式实现这一点。服务器创建的“nonce”值依赖于实现,但是如果它包含客户端IP的摘要、时间戳、资源ETag和私有服务器密钥(如上所述),那么重放攻击就不简单了。
  • 攻击者必须使服务器相信请求来自一个错误的IP地址,并且必须使服务器将文档发送到与它认为要发送文档的地址不同的IP地址。攻击只能在时间戳过期之前成功。
    在nonce中消化客户端IP和时间戳允许一个不维护事务之间状态的实现。
  • 对于不能容忍重放攻击的应用程序,服务器可以使用一次性的非一次性值,这不会被允许第二次使用。这需要服务器的开销
    记住在nonce时间戳(以及用它构建的摘要)过期之前使用了哪些nonce值,但它有效地防止了重放攻击。
  • 实现必须特别注意使用POST和PUT请求重放攻击的可能性。除非服务器使用一次性或其他限制使用的nonces和/或坚持使用qop=auth-int完整性保护,否则攻击者可以用伪造的表单数据或其他消息体重发成功请求的有效凭证。即使使用了完整性保护,报头字段中的大部分元数据也没有得到保护。
    正确的nonce生成和检查提供了一些保护,防止重复使用以前使用的有效凭证,但请参见4.8。

4.6 多个认证方案产生的缺陷

  • HTTP/1.1服务器可能返回多个带有401 (Authenticate)响应的挑战,并且每个挑战可能使用不同的auth-scheme。
    用户代理必须选择使用它所理解的最强认证方案,并根据该挑战向用户请求凭据。
  • 注意,许多浏览器只会识别Basic,并要求它是第一个提供的auth-scheme。服务器应该只在最低限度可接受的情况下包含Basic。
  • 当服务器使用WWW-Authenticate头提供身份验证方案的选择时,所得到的身份验证的强度只相当于最弱的身份验证方案的强度。
    关于利用多种身份验证方案的特定攻击场景的讨论,请参见下面的第4.8节。

4.7 在线字典攻击

  • 如果攻击者可以窃听,那么它就可以对常见单词列表测试任何被窃听的nonce/response对。这样的列表通常比可能的密码总数要小得多。计算列表中每个密码的响应的成本是为每个挑战支付一次。服务器可以通过不允许用户选择字典中的密码来减轻这种攻击。

4.8 中间人攻击

  • 基本身份验证和摘要身份验证都容易受到“中间人”(MITM)攻击,例如来自敌对的或被破坏的代理的攻击。
  • 显然,这将带来窃听的所有问题。但它也为攻击者提供了一些额外的机会。一种可能的中间人攻击是向选择集添加一个弱身份验证方案,希望客户机将使用一个暴露用户凭据(例如密码)的方案。出于这个原因,客户应该总是使用从提供的选择中所能理解的最强大的方案。更好的MITM攻击是删除所有提供的选择,用只请求基本身份验证的挑战替换它们,然后使用来自基本身份验证的明文凭据,使用它请求的更强的方案向源服务器进行身份验证。
  • 安装这种MITM攻击的一种特别阴险的方式是向易受骗的用户提供“免费的”代理缓存服务。用户代理应该考虑一些措施,比如在请求凭证时显示将要使用的身份验证方案的可视指示,或者记住服务器曾经请求过的最强身份验证方案,并在使用较弱的身份验证方案之前产生警告消息。
  • 将用户代理配置为通常要求Digest身份验证,或者从特定站点要求Digest身份验证,这也可能是一个好主意。或者,恶意代理可能欺骗客户机发出攻击者想要的请求,而不是客户机想要的请求。当然,这仍然比针对基本身份验证的类似攻击困难得多。

4.9 可选的明文攻击

  • 使用Digest身份验证,MITM或恶意服务器可以任意选择客户机将使用nonce来计算响应。这被称为“选择明文”攻击。
  • 选择nonce的能力使密码分析更容易[8]。
    但是,目前还不知道如何使用所选的明文分析Digest使用的MD5单向函数。
    针对这种攻击的对策是将客户端配置为需要使用可选的“cnonce”指令;这允许客户端以攻击者不选择的方式改变散列的输入。

4.10 预估字典攻击

  • 使用Digest身份验证,如果攻击者可以执行选择的明文攻击,那么攻击者可以对其选择的nonce预先计算许多常见单词的响应,并存储一个(响应、密码)对字典。这种预计算通常可以在许多机器上并行完成。然后,它可以使用所选的明文攻击获取对应于该挑战的响应,并在字典中查找密码。
  • 即使大多数密码不在字典中,也有一些可能在。既然攻击者可以选择挑战,那么计算列表中每个密码的响应的代价可以分摊到寻找许多密码上。一个包含1亿个密码/响应对的字典大约需要3.2 gb的磁盘存储。对付这种攻击的对策是,将客户端配置为需要使用可选的“cnonce”指令。

4.11 批量暴力破解攻击

  • 使用摘要身份验证,MITM可以执行选择的明文攻击,并可以收集来自许多用户对同一个nonce的响应。
  • 然后,它可以找到密码空间的任何子集中的所有密码,这些密码空间将在通过该空间的一次传递中生成一个nonce/response对。
    它还将查找第一个密码的时间缩短了一个因数,该因数等于收集到的nonce/response对的数量。
  • 这种密码空间的搜索通常可以在许多机器上并行完成,甚至一台机器也可以非常快速地搜索密码空间的大子集——有报告说,可以在几个小时内搜索包含6个或更少字母的所有密码。
  • 对付这种攻击的对策是,将客户端配置为需要使用可选的“cnonce”指令。

4.12 仿冒服务器欺骗

  • 基本身份验证容易受到假冒服务器的欺骗。如果一个用户可以相信她是连接到一个包含信息保护的密码她知道主机,而事实上她是连接到一个充满敌意的服务器,然后充满敌意的服务器可以请求一个密码,将其存储起来供以后使用,假装一个错误。
  • 使用摘要身份验证时,这种类型的攻击更加困难——但是客户机必须知道要求使用摘要身份验证,可能需要使用上面描述的一些技术来对抗“中间人”攻击。
  • 同样,通过使用的身份验证机制的可视化指示,以及解释每个方案含义的适当指导,可以帮助用户检测这种攻击。

4.13 储存密码

  • 摘要身份验证要求身份验证代理(通常是服务器)在与给定域关联的“密码文件”中存储来自用户名和密码的一些数据。
  • 通常,这可能包含由用户名和H(A1)组成的对,其中H(A1)是如上所述的用户名、领域和密码的摘要值。
  • 这样做的安全影响是,如果这个密码文件被破坏了,那么攻击者就可以使用这个域立即访问服务器上的文档。与标准的UNIX密码文件不同,为了访问与该文件关联的服务器域中的文档,不需要对该信息进行解密。另一方面,解密,或者更可能的暴力攻击,将是获取用户密码的必要条件。这就是域是存储在密码文件中的摘要数据的一部分的原因。这意味着如果一个Digest身份验证密码文件被盗用,它不会自动被盗用具有相同用户名和密码的其他文件(尽管它确实会将它们暴露给暴力攻击)。
  • 这有两个重要的安全后果。首先,必须保护密码文件,就像它包含未加密的密码一样,因为为了访问其域内的文档,它实际上是这样做的。这样做的第二个结果是,领域字符串在任何单个用户可能使用的所有领域中都应该是唯一的。特别是,领域字符串应该包括执行身份验证的主机的名称。客户端无法对服务器进行身份验证是摘要身份验证的一个弱点。

4.14 总结

  • 根据现代密码标准,摘要身份验证是弱的。但是对于很多目的来说,它作为基本身份验证的替代品是很有价值的。它弥补了一些(但不是全部)基本身份验证的弱点。它的强度可能因实现而异。特别是nonce的结构(依赖于服务器实现)可能会影响挂载重放攻击的容易程度。
  • 一系列的服务器选项是适当的,因为,例如,一些实现可能愿意接受一次性nonce或摘要的服务器开销,以消除重放的可能性。
    其他人可能会满意像上面推荐的限制到一个单一IP地址和单一ETag或有限的生命周期的nonce的底线是,根据密码标准,任何兼容的实现都是相对薄弱的,但是任何兼容的实现都将远远优于基本身份验证。

5、样本实现

下面的代码实现了H(A1)、H(A2)、请求摘要和响应摘要的计算,以及一个计算3.5节示例中使用的值的测试程序。
它使用RFC 1321的MD5实现。
文件 “digcacl.h”

#define HASHLEN 16
typedef char HASH[HASHLEN];
#define HASHHEXLEN 32
typedef char HASHHEX[HASHHEXLEN+1];
#define IN
#define OUT

/* 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,
    OUT HASHHEX SessionKey
    );

/* calculate request-digest/response-digest as per HTTP Digest spec */
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" */
    OUT HASHHEX Response      /* request-digest or response-digest */
    );

文件 "digcacl.c"

#include <global.h>
#include <md5.h>
#include <string.h>
#include "digcalc.h"

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';
};

/* 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_CTX Md5Ctx;
      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);
};

/* calculate request-digest/response-digest as per HTTP Digest spec */
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" */
    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));
          MD5Update(&Md5Ctx, ":", 1);
      };
      MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
      MD5Final(RespHash, &Md5Ctx);
      CvtHex(RespHash, Response);
};

文件 “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 = "testrealm@host.com";
      char * pszPass = "Circle Of Life";
      char * pszAlg = "md5";
      char szNonceCount[9] = "00000001";
      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);
      printf("Response = %s\n", Response);
};

==================>略

6 鸣谢

7 参考

8 作者地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容