但它还是比在LDAP[10]、POP及IMAP(见rfc2195[9])上用的CRAM-MD5要强许多。它将被用来替代薄弱的危机四伏的基本机制。
分类鉴别只提供对实际口令的加密保护。请求或回应的所有其它内容都可以被监听。
对双向传递的消息,分类鉴别方案只提供有限的完整性保护。如果使用了qop=auth-int机制,消息中参与计算WWW-鉴别和授权标题域中的回应指示值(见上面的3.2节)的部分将受到保护。大多数标题域及其值都可被修改,从而成为中间层攻击的组成部分。
分类鉴别方案不足以满足许多安全HTTP事务的需要。为此,TLS或SHTTP成为更适合的协议。分类鉴别尤其不能用于许多需要加密保护的事务处理上。尽管如此,许多功能仍得以保留,所以分类鉴别仍然可以继续使用。任何现存的对基本鉴别方案的服务都应根据实际情况尽早转到分类鉴别方案上。
XML:namespace prefix = o ns = "urn:schemas-microsoft-com:Office:office" />
4.3 受限的nonce值使用(Limited Use Nonce Values)
分类方案使用服务器指定的nonce值为种子生成请求-分类(request-digest)值(见前面3.2.2.1)。如3.2.1节例子所示的nonce,服务器可任意构造它,而它只能用于指定的客户、指定的资源、有限的时间段或使用数量及其它限制。这样做会增强系统保护机制,比如可以抵抗回放式攻击(relay attack,见4.5)。然而,应当看到,选择生成或检测nonce的方法也存在些性能问题及资源消耗。例如,服务器可以通过记录列表检查最近发出的nonce是否返回,并在每个回应的Authentication-Info标题域中发送next-nonce指示,从而实现每个nonce值只能使用一次。这种措施可以抵挡哪怕是立即方式的回放攻击,但是用于检查nonce值的开销也非常高,甚至可能造成管道式请求的鉴别失败(假设返回nonce过期指示)。类似情况,要合并请求指定的元素,如资源的Etag值,也将限制对应版本资源中nonce的使用,从而导致管道失败。因而,此种方案虽然看来有时很有效,但从性能上看,对没采用此方案的一方来说,将是不可接受的。
Franks, et al. Standards Track [Page 21]
4.4 基本鉴别与分类鉴别的比较(Comparison of Digest with Basic Authentication)
分类鉴别和基本鉴别都是处于安全体系的薄弱端,两者比较的意义在于可在必要时用分类鉴别替代基本鉴别。
事务在网络协议上传输的最大危胁在于网络监听。这些事务可能包含一些与交易相关的数据库在线访问。基本鉴别方案下,偷听者可以得到用户的口令,这样,他就可以在数据库中做任何操作,而且更糟糕的是,该用户使用相同口令保护的一切资源都将受到危胁。
与上面相应,如果采用分类鉴别方案,偷听者最多只能得到事务的访问权而得不到用户口令。偷听者所得到的信息将允许他进行回放式攻击,但只能请求同一个文件,而如果服务器端对nonce的选项进行限制的话,他的请求将不能得逞。
4.5 回放式攻击(Replay Attacks)
对简单的GET请求而言,对分类鉴别方案实行回放式攻击是没有什么意义的,因为偷听者早已通过回放得到了他所能得到的唯一文件。这是因为被请求文件的URI已经在客户端请求中进行了分类,服务器将只传递这个指定文件。而在基本鉴别方案下则不同,偷听者会得到用户的口令,从而得到受此口令保护的任何文件。
因而,从某种目的上看,防范回放式攻击还是必要的。一个好的分类鉴别的实现可以通过多种方式来解决这个问题。服务器产生nonce值是与实现无关的,但是如果它包含了客户IP、时间戳、资源的Etag及私有服务器密钥(如上面所建议的),将增大回放式攻击的难度。攻击方必须必须让服务器相信请求是来自一个错误的IP地址,从而导致服务器将要发送的文件发到偷听者指定的IP而不是原来的IP地址。 攻击方只有在时间戳过期前才有可能取得成功。对nonce中的客户IP及时间戳进行分类将允许实现程序不必对事务间的状态进行维护。
对应用程序而言,如果无法容忍可能存在的回放式攻击,可使用时间少于1秒的一次性nonce值。当然,这会增加服务器的额外开销,如,服务器 要记住在nonce时间戳(用来进行分类的)过期前,哪些nonce值已经用过了。不过对于回放式攻击,该方法很有效。
Franks, et al. Standards Track [Page 22]
实现程序要防范可能存在的、使用POST或PUT请求进行的回放式攻击。如果服务器没有使用一次性或限制性的nonce或(和)用qop=auth-int的完整性保护,攻击方仍然可以通过假冒表格数据(foRM data)或其它消息主体的方式来构造一个带有合法信任信息的请求,从而成功地实现回放攻击。即使采用完整性保护,标题域中的大多数元数据仍不在保护范围之内。实践中,正确产生nonce,并对提供的保护进行检查,可以有效防范利用先前用过的合法信任来进行的回放式攻击。见4.8。
4.6 由多方鉴别方案产生的弱点(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)时得到回报。
服务器可以采取措施,不允许用户使用字典中的单词做为口令,这样可以降低这种攻击的危险。
Franks, et al. Standards Track [Page 23]
4.8 中间人(Man in the Middle)
基本鉴别方案和分类鉴别方案都容易受到‘中间人’(man in the middle,MITM)的攻击。例如,怀有敌意或不安全的代理(Proxy)。可以肯定地说,它与所有偷听类问题相关。而且,它还为攻击者提供了一些额外的机会。
‘中间人’攻击可能从一组鉴别方案中的薄弱环节入手,并希望客户端使用了已经暴露的用户信任(如口令)。出于这种原因,客户端应尽可能从提供的选项中选择它所支持的最强鉴别方案。
更高级的‘中间人’攻击可能会删除提供的鉴别方案选项,而替换成一个采用基本鉴别方案的质询,这样用户和原始服务器交互的信任将使用明文来传输。还有个更阴险的方法,就是采用‘免费’代理缓存服务来欺骗用户。
当通过鉴别的信任请求被使用时,用户代理(agent)应当考虑其界面显示的尺度,而且应记着当发现服务器要求高级别而回应是低级别时,向用户发出报警信息。另一个不错的主意是,将用户代理配置成分类鉴别方式,或转接到其它安全站点上。另外,有敌意的代理(proxy)还可以伪装成客户端发出请求,当然,和基本鉴别方案相比,还是有一定难度的。
4.9 选择纯文本攻击(Chosen plaintext attacks)
分类鉴别方案下,‘中间人’或有恶意的服务器可以任意选择客户用来计算回应的nonce值。这种方式被称为‘选择纯文本’攻击。选择已知的nonce可以使密码分析更加容易[8]。
实际上,对采用纯文本进行分类的功能进行单向分析是不可能的。
客户端对抗此攻击的对策是,在配置中要求使用”cnonce”指示;这将允许客户对输入值按照自己的方式而不是攻击者指定的方式进行哈希变换。
Franks, et al. Standards Track [Page 24]
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)
基本鉴别方案容易受到假冒服务器攻击。当用户坚信他正与一台受基本鉴别方案保护的主机相连时,他也许不会想到,此时他可能正与怀有敌意的服务器相连。攻击者可截获口令,并将其储存起来备用,同时假装返回一个错误。在分类鉴别方案下,这种攻击实现下来要难一些,但前提是客户端必须要求使用分类鉴别方案,或采用上面提到的一些技术来统计‘中间人’攻击。另外,用户使用的鉴别机制也会在发现这种攻击时给用户提个醒。
Franks, et al. Standards Track [Page 25]
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的寿命周期等等,来满足安全方面的需求。
Franks, et al. Standards Track [Page 26]
密码安全的极限,或说底线(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
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, //计算方式:md5-sess或md5
IN char * pszUserName, //用户名
IN char * pszRealm, //realm
IN char * pszPassword, //口令
IN char * pszNonce, //nonce
IN char * pszCNonce, //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
#include
Franks, et al. Standards Track [Page 27]
#include
#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] = '';
};
/* 计算H(A1),calculate H(A1) as per spec */
void DigestCalcHA1(
IN char * pszAlg, //计算方式:md5-sess或md5
IN char * pszUserName, //用户名
IN char * pszRealm, //realm
IN char * pszPassword, //口令
IN char * pszNonce, //nonce
IN char * pszCNonce, //cnonce
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) {
Franks, et al. Standards Track [Page 28]
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" */
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) {
Franks, et al. Standards Track [Page 29]
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);
};
File "digtest.c":
#include
#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 ", Response);
};
Franks, et al. Standards Track [Page 30]
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10790690/viewspace-952981/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/10790690/viewspace-952981/