1. 什么是摘要認證


摘要認證與基礎認證的工作原理很相似,用戶先發出一個沒有認證證書的請求,Web服務器回復一個帶有WWW-Authenticate頭的響應,指明訪問所請求的資源需要證書。但是和基礎認證發送以Base?64編碼的用戶名和密碼不同,在摘要認證中服務器讓客戶端選一個隨機數(稱作”nonce“),然后瀏覽器使用一個單向的加密函數生成一個消息摘要(message?digest),該摘要是關于用戶名、密碼、給定的nonce值、HTTP方法,以及所請求的URL。


2. 摘要認證算法


摘要認證規范最早定義在RFC 2069中,RFC 2069定義了由服務器生成隨機數來維護安全性的摘要認證架構,認證結果是采用下列方法得出的:

HA1=MD5(username:realm:password)
HA2=MD5(method:digestURI)
response=MD5(HA1:nonce:HA2)


后來,RFC 2617引入一些可選的增強安全的方法代替了RFC 2069,這些增強的方法包括質量保護(QOP),客戶端自增加計數器和客戶端生成的隨機數。這些可選參數提高了摘要認證的安全性,如可防止明文***。


如果算法指令是“MD5”或者未指定,HA1算法如下:

HA1=MD5(username:realm:password)


如果算法指令是“MD5-sess”,HA1算法

HA1=MD5(MD5(username:realm:password):nonce:cnonce)


如果質量保護指令是“auth”或未指定,HA2算法是

HA2=MD5(method:digestURI)


如果質量保護指定是“auth-int”,HA2算法是

HA2=MD5(method:digestURI:MD5(entityBody))


如果質量保護指定是“auth”或者“auth-int”,響應結果算法是

response=MD5(HA1:nonce:nonceCount:cnonce:qop:HA2)


如果質量保護指令未指定,響應結果算法是

response=MD5(HA1:nonce:HA2)


3. MD5安全問題對摘要認證的影響


雖然MD5是可逆的,被認為是不安全的,但是在HTTP摘要認證過程中使用MD5算法中引入了一些隨機數,使得數據的可逆性的難度大大提高,所以使用MD5是安全的。但如果用戶的密碼過于簡單,通過字典或排序查找算法破解難度就大大降低了,因此不建議使用過于簡單的密碼。


RFC2617 的安全增強的主要方式:


發起請求的時候,服務器會生成一個密碼隨機數(nonce)(而這個隨機數只有每次"401"相應后才會更新),為了防止***者可以簡單的使用同樣的認證信息發起老的請求,于是,在后續的請求中就有一個隨機數計數器(cnonce),而且每次請求必須必前一次使用的打.這樣,服務器每次生成新的隨機數都會記錄下來,計數器增加.在RESPONSE 碼中我們可以看出計數器的值會導致不同的值,這樣就可以拒絕掉任何錯誤的請求.


4. 摘要認證流程


  • 客戶端請求一個需要認證的頁面,但是不提供用戶名密碼

  • 服務器返回401?"Unauthorized" 響應代碼,并提供認證域(realm),以及一個隨機生成的、只使用一次的數值,稱為密碼隨機數 nonce

  • 瀏覽器會向用戶提示認證域(realm)(通常是所訪問的計算機或系統的描述),并且提示用戶名和密碼。

  • 一旦提供了用戶名和密碼,客戶端會重新發送同樣的請求,但是添加了一個認證頭包括了響應代碼。

  • 服務器接受了認證并且返回了請求頁面。如果用戶名非法和/或密碼不正確,服務器將返回"401"響應代碼,然后客戶端會再次提示用戶輸入用戶名及密碼。


5.一段摘要加密報文示例


有興趣的用戶可以根據摘要加密算法進行計算。

Authorization:?

Digest?username="admin", ? ? //用戶名

realm="favourite?digest?realm", ? //用戶認證提示

nonce="1478047635099:8f32add4b6aaef23649ca330b25c64be", ?//隨機數

uri="/hello", ?//請求地址

response="778110c2b5d0215e0d7640a6baa069a0", ? //加密后的值

opaque="B2E49B2542089EE534695CA7742FB588", ?

qop=auth, ? //質量保護

nc=00000001, ? //客戶端自增加隨機數計數器

cnonce="8664dcaabed7e083" //客戶端的隨機數


HA1=MD5("admin:favourite?digest?realm:password");

HA2=MD5("GET:"+uri);

response =?MD5(HA1:nonce:nc:cnonce:qop:HA2)