一,、前言
HTTP認(rèn)證是Web服務(wù)器對客戶端的權(quán)限進(jìn)行認(rèn)證的一種方式,,能夠為Web應(yīng)用提供一定程度的安全保障。目前一些Web應(yīng)用項目已經(jīng)提出了采用HTTP認(rèn)證的需求,。雖然一般的Web容器都提供基本認(rèn)證和摘要認(rèn)證的API,,但不同的Web容器提供的API也互不相同,因此我們在ZX Web平臺的工具包中提供了一組API,,利用這組API,,開發(fā)人員可以在應(yīng)用程序中使用統(tǒng)一的接口輕松實現(xiàn)HTTP認(rèn)證功能,而不必依賴于Web容器,。
二,、HTTP認(rèn)證機(jī)制
HTTP認(rèn)證采用“質(zhì)詢-響應(yīng)(challenge-response)”的機(jī)制。“質(zhì)詢”是服務(wù)器端對客戶端的質(zhì)詢,,即要求客戶端發(fā)送認(rèn)證信息,;“響應(yīng)”是客戶端對“質(zhì)詢”的響應(yīng),即發(fā)送帶有認(rèn)證信息的HTTP請求,。 一般來說,,客戶端第一次請求一個URI時,并不知道是否需要認(rèn)證,,因此總是不帶認(rèn)證信息的,,這時服務(wù)器端就會找不到認(rèn)證信息,認(rèn)證失敗,,于是向客戶端發(fā)出一個“質(zhì)詢”,。 所謂“發(fā)出質(zhì)詢”,就是給客戶端發(fā)送一個HTTP響應(yīng),,其狀態(tài)碼為401 (Unauthorized),,并且包含消息頭WWW-Authenticate,客戶端看到這個響應(yīng)就知道這個URI需要認(rèn)證,。WWW-Authenticate消息頭格式為 WWW-Authenticate:challenge 其中<challenge>是就是質(zhì)詢信息,,RFC2617中的定義為: challenge = auth-scheme 1*SP 1#auth-param auth-scheme = token auth-param = token "=" ( token | quoted-string ) 在challenge的定義中,首先是auth-scheme,,即認(rèn)證方案,,它被定義為一個token,,即預(yù)定義的符號。所謂token,,就是一些字符串,,但這些字符串不是隨意的,而是大家約定的,,它們具有特定的含義,。auth-scheme的取值只能是Basic或Digest,分別表示基本認(rèn)證和摘要認(rèn)證,,這兩個單詞就是token,。這里沒有把a(bǔ)uth-scheme定義為Basic|Digest,而是一個token,,說明還可以進(jìn)行擴(kuò)展,,還可以取其他符號——只要服務(wù)器端和客戶端互相約定都能理解就行。 接著,,“1*SP”表示1個或多個空格符,。其中“1*”表示數(shù)量為1個到多個,“SP”即空格符(ASCII碼32),。 然后是“1#auth-param”,表示一個auth-param的列表,。其中的“1#”也表示后面的元素是1到多個,,但與“1*”不同的是,“1#”表示一個“列表”,,即元素之間是用逗號“,,”分隔開的。列表中的每個auth-param被定義為一個名值對,,即 符號=符號 或 符號=“引號中的字符串” 這兩種形式,。 基本認(rèn)證和摘要認(rèn)證中都定義了一個相同的auth-param,即realm,,定義為: realm = “realm”“=” realm-value realm-value = quoted-string realm-value是一個兩端加引號的大小寫相關(guān)的字符串,,表示要求認(rèn)證的“領(lǐng)域(realm)”。領(lǐng)域是由服務(wù)器自己決定的,,不同的服務(wù)器可以設(shè)置自己的領(lǐng)域,,同一個服務(wù)器也可以有多個領(lǐng)域。質(zhì)詢中包含領(lǐng)域信息是為了讓客戶端知道哪個范圍的用戶名是合法的,,RFC2617中建議領(lǐng)域至少包含主機(jī)名和有權(quán)限的用戶組,,例如“[email protected]”。 客戶端收到質(zhì)詢后,,應(yīng)該給服務(wù)器端返回一個“響應(yīng)”,,即重新發(fā)送一個新的HTTP請求,。這個新的HTTP請求與前一個HTTP請求的差別在于多了一個Authorization消息頭,該消息頭的格式為Authorization:credentials,,其中的credentials就是認(rèn)證信息,,認(rèn)證信息的格式根據(jù)不同的認(rèn)證方案而有所不同。 服務(wù)器端對認(rèn)證信息進(jìn)行判斷,,只有認(rèn)證通過,,才會響應(yīng)客戶端的請求。 1. 基本認(rèn)證 基本認(rèn)證的質(zhì)詢中只定義了一種auth-param,,即realm,,因此基本認(rèn)證的質(zhì)詢也定義為 challenge = “Basic” realm 質(zhì)詢舉例: 當(dāng)服務(wù)器端認(rèn)證不通過,將返回一個狀態(tài)碼為401(Unautherized)的響應(yīng)消息,,并帶有如下消息頭: WWW-Authenticate: Basic realm=“My Secret World” 基本認(rèn)證的認(rèn)證信息credentials定義為: credentials = “Basic” basic-credentials basic-credentials = base64-user-pass base64-user-pass = <base64 encoding of user-pass, except not limited to 76 char/line> user-pass = userid “:” password userid = * password = *TEXT 簡單說,,認(rèn)證信息就是“Basic”后面加上“<用戶名>:<密碼>”的Base64編碼,只不過這里的Base64編碼不對每一行的字符數(shù)做最大76個的限制,。 認(rèn)證信息舉例: 如果用戶名為“abc”,,密碼為“abcd”,將“abc:abcd”進(jìn)行Base64編碼得到“YWJjOmFiY2Q=”,,于是消息頭中認(rèn)證信息為 Authorization: Basic YWJjOmFiY2Q= 2. 摘要認(rèn)證 由于基本認(rèn)證被認(rèn)為是不安全的認(rèn)證方式,,摘要認(rèn)證作為替代方案被制定了出來。摘要認(rèn)證中,,用戶名和密碼不會以明文方式傳送,,而是經(jīng)過了加密。從名稱可以看出,,是生成了信息摘要,,客戶端和服務(wù)器使用各自的密碼以同樣的算法生成信息摘要,兩者比較即可判斷客戶端的密碼是否正確,。 摘要認(rèn)證仍然采用WWW-Authenticate和Authorization兩個消息頭,,另外還規(guī)定了消息頭Authentication-Info。消息頭Authentication-Info用于認(rèn)證通過之后,,服務(wù)器給客戶端返回一些信息,,例如可以用來指定下一次認(rèn)證用的臨時值,或者也生成一個摘要,,表明服務(wù)器確實知道用戶密碼,,等等。不過這個消息頭并不是必須的,,實際應(yīng)用中一般也用不著,,因此Web平臺中目前沒有實現(xiàn),這里也不做介紹,,若有興趣請查看RFC2617,。
三,、HTTP認(rèn)證的安全性
1. 基本認(rèn)證的安全性 基本認(rèn)證不是一種安全的認(rèn)證方式,因為Base64編碼僅僅是編碼,,而不是加密,,以這種形式在互聯(lián)網(wǎng)上傳遞用戶名和密碼,其危險性是顯而易見的,。但如果對安全性要求不高,,則可以使用這種認(rèn)證方式做為最簡單的安全措施--畢竟比沒有安全措施要好。 當(dāng)然,,如果能夠保證中間不會有人截取數(shù)據(jù)包,,例如處于內(nèi)部局域網(wǎng),或者底層協(xié)議是安全的(如使用SSL或其他一些安全機(jī)制),,倒是可以彌補(bǔ)HTTP基本認(rèn)證在安全性方面的不足,。 2. 摘要認(rèn)證的安全性 由于基本認(rèn)證過于危險,人們才使用摘要認(rèn)證作為一種替代方案,。但它也僅僅是作為基本認(rèn)證的替代品,,因為它本身也不是十分安全的,也存在一些弱點,。 (1)摘要認(rèn)證只能作為權(quán)限認(rèn)證機(jī)制,,并非保密措施,因為消息體并沒有被加密,。qop使用“auth-int”只能保證消息體不被修改,,不能防止被偷看。 (2)Replay攻擊:攻擊者可能截取一次摘要信息,,然后利用相同的摘要信息請求相同的URI,如果該URI可以通過POST或PUT方法訪問,,則攻擊者可能修改消息體,。控制nonce中的時間戳和nc次數(shù)有助于減小replay攻擊機(jī)會,;每次使用新的nonce值(用Authentication-Info消息頭)可避免遭受replay攻擊,,當(dāng)然也增加了開銷。 (3)MITM(Man in the Middle)攻擊:攻擊者截取網(wǎng)絡(luò)數(shù)據(jù)包,,給客戶端發(fā)送一個假的質(zhì)詢,,只要求客戶端使用基本認(rèn)證,從而取得密碼,。MITM最常見的方式是提供一個“免費(fèi)”的但其實是惡意的代理服務(wù)器,。要防止此類攻擊,可雙方約定只使用摘要認(rèn)證,,不允許使用基本認(rèn)證,,但一般瀏覽器并不支持指定認(rèn)證方式,,除非是自己開發(fā)的客戶端。 其他還有些攻擊方式,,例如通過“查字典”猜密碼等比較野蠻的方式,。雖然摘要認(rèn)證有這些弱點,但在許多情況下還是有它的使用價值的,,至少比基本認(rèn)證是好多了,。
四、HTTP認(rèn)證在ZX Web平臺中的實現(xiàn)
在ZX Web平臺的包中提供了HTTP認(rèn)證的API,,這組API包括一個接口HttpAuth以及該接口的兩個實現(xiàn)類HttpBasicAuth和HttpDigestAuth,,分別實現(xiàn)基本認(rèn)證和摘要認(rèn)證。開發(fā)人員可以使用接口,,也可以直接使用兩個實現(xiàn)類,。類圖如圖1所示。 1. HttpAuth接口 這個接口應(yīng)該提供一個方法取得用戶提交的用戶名和密碼,,以便應(yīng)用程序校驗其正確性,。在基本認(rèn)證中,這一點是可以做到的,,但在摘要認(rèn)證中服務(wù)器端并不能知道客戶端提交的密碼,,所能得到的只是對包含密碼的數(shù)據(jù)進(jìn)行MD5編碼所得到一個摘要,因此為了照顧摘要認(rèn)證,,HttpAuth接口沒有設(shè)計這樣一個方法,,而是提供另一個方法authenticate,調(diào)用者將用戶名和正確的密碼作為該方法的參數(shù)傳入,,返回認(rèn)證結(jié)果,。該方法還需要另一個參數(shù),即HTTP請求HttpServletRequest的一個實例,,因為需要從中取得用戶提交的認(rèn)證信息,;HttpAuth接口的所有方法都需要這個參數(shù)。 在大多數(shù)情況下,,合法的用戶不止一個,,因此調(diào)用authenticate方法之前必須知道客戶端提交的用戶名,。HttpAuth接口的getUserName方法完成此功能。這兩個方法描述如下: getUserName 功能: 取得用戶發(fā)來的認(rèn)證信息中的用戶名。 原型: public String getUserName(HttpServletRequest req) 參數(shù): req - HTTP請求 返回: 返回認(rèn)證信息中的用戶名。若未取到(請求中未包含認(rèn)證信息或認(rèn)證信息格式不正確),,返回null。 authenticate 功能: 判斷一個HTTP請求的認(rèn)證信息中用戶名和密碼是否與指定的相符。 原型: public boolean authenticate(HttpServletRequest req, String userName, String password) 參數(shù): req - HTTP請求 userName - 指定的用戶名 password - 指定的密碼 返回: true - 認(rèn)證通過 false - 認(rèn)證未通過 當(dāng)調(diào)用authenticate方法發(fā)現(xiàn)認(rèn)證未通過,,應(yīng)該給客戶端發(fā)回一個質(zhì)詢,,即設(shè)置響應(yīng)的狀態(tài)碼為401,并設(shè)置消息頭WWW-Authenticate,。HttpAuth接口的setUnauth方法負(fù)責(zé)完成此功能,。 SetUnauth 功能: 設(shè)置 401 Unauthorized 狀態(tài)碼,,并添加WWW-Authenticate消息頭。 原型: public void setUnauth(HttpServletRequest req, HttpServletResponse rsp, String realm) 參數(shù): req - HTTP請求 rsp - HTTP響應(yīng) realm - WWW-Authenticate消息頭中的realm值,。若為null,,則默認(rèn)使用 servername:port 返回:無 setUnauth方法設(shè)置HTTP響應(yīng)對象rsp的狀態(tài)碼為401,,并添加WWW-Authenticate消息頭,至于消息頭的內(nèi)容,,HttpBasicAuth類和HttpDigestAuth類根據(jù)基本認(rèn)證和摘要認(rèn)證的規(guī)范不同而有不同的實現(xiàn),。設(shè)置WWW-Authenticate消息頭需要realm值,調(diào)用者通過realm參數(shù)指定realm值,。調(diào)用者如果不想指定realm值,可以置realm參數(shù)為null,,或直接調(diào)用另一種參數(shù)形式的setUnauth方法: public void setUnauth(HttpServletRequest req, HttpServletResponse rsp) 如果不指定realm值,,setUnauth方法將從請求對象req中取得服務(wù)器名和端口,按servername:port組合,,以此作為realm的值,。 最后,HttpAuth接口提供getAuth方法,,以取得HTTP請求中用戶提交的認(rèn)證信息,,即Authorization消息頭中"Basic"或"Digest"標(biāo)志之后的信息;若是基本認(rèn)證,,此字符串是BASE64編碼的,則返回解碼后的字符串,。對開發(fā)人員來說,,此方法不是必須的,但可以用于調(diào)試,。 getAuth 功能: 取得用戶提交的認(rèn)證信息。 原型: public String getAuth(HttpServletRequest req) 參數(shù): req - HTTP請求 返回: 用戶提交的摘要認(rèn)證信息中"Digest"或"Basic"標(biāo)識之后信息;若是BASE64編碼,,返回解碼后的字符串。 2. HttpBasicAuth類 HttpBasicAuth類實現(xiàn)HTTP基本認(rèn)證。 HttpBasicAuth除了實現(xiàn)HttpAuth接口的所有方法外,還增加一個方法getUserNamePwd,用以取得客戶端提交的用戶名和密碼,。在某些情況下,,服務(wù)器端的數(shù)據(jù)庫或文件中并沒有保存用戶密碼的明文,,而是保存對密碼經(jīng)過某種不可逆加密算法(MD5或其他)而得到的信息摘要(如UNIX系統(tǒng))。如果是這樣,,服務(wù)器端無法調(diào)用authenticate方法來進(jìn)行認(rèn)證,,只能先調(diào)用getUserNamePwd方法取得HTTP請求中的用戶名和密碼,然后按照數(shù)據(jù)庫或文件中的相同加密算法計算其摘要,,最后比較所得到的摘要是否與數(shù)據(jù)庫或文件中的相同,。這種情況下不能使用摘要認(rèn)證,只能使用基本認(rèn)證,。 getUserNamePwd 功能: 取得用戶名密碼字符串?dāng)?shù)組,。 原型: public String[] getUserNamePwd(HttpServletRequest req) 參數(shù): req - HTTP請求 返回: 字符串?dāng)?shù)組,第一個元素為用戶名,,第二個元素為密碼,;若未取到用戶名和密碼,返回null,。 HttpBasicAuth只有一個成員變量: protected static BASE64Decoder base64Decoder = new BASE64Decoder(); 即sun.misc.BASE64Decoder類的一個實例,,用于BASE64解碼。 3. HttpDigestAuth類 HttpDigestAuth類實現(xiàn)HTTP摘要認(rèn)證。HttpDigestAuth類除了實現(xiàn)HttpAuth接口外,,根據(jù)摘要認(rèn)證的特點,,還提供了其他一些方法。 客戶端提交的認(rèn)證信息中,,有一個nc值,,表示臨時值nonce已被使用的次數(shù);HttpDigestAuth類的方法getNonceCount可取得此值,。 getNonceCount 功能: 取得用戶發(fā)來的認(rèn)證信息中的nc(NonceCount)值,。 原型: public int getNonceCount(HttpServletRequest req) 參數(shù): req - HTTP請求 返回: 返回nc值。若未取到,,返回0,。 對于這個nc值,服務(wù)器端根據(jù)自己的策略可以選擇不作限制,,也可限定一個最大值,。如果發(fā)現(xiàn)摘要正確但nonce使用次數(shù)超過上限,可以給客戶端返回一個“過期”質(zhì)詢響應(yīng),,其WWW-Authorize消息頭中包含一個新的nonce值,,并設(shè)置stale字段為true,以此要求客戶端使用新的nonce值重新計算摘要,。此時客戶端不會重新彈出對話框讓用戶輸入密碼,,而是用原來的密碼和新的nonce值重新計算摘要,然后重新發(fā)出請求,。對客戶端用戶來說,,這一過程是透明的。HttpDigestAuth提供一種參數(shù)形式的setUnauth方法,,用以給客戶端返回“過期”質(zhì)詢,。 setUnauth 功能: 設(shè)置 401 Unauthorized 狀態(tài)碼,并添加WWW-Authenticate消息頭,。 原型: public void setUnauth(HttpServletRequest req, HttpServletResponse rsp, String realm, boolean stale) 參數(shù): req - HTTP請求 rsp - HTTP響應(yīng) realm - 指定realm字段的值,,若為null則采用默認(rèn)值servername:port stale - 指定stale字段的值,true表示客戶端摘要正確,,只是nonce值過期導(dǎo)致鑒權(quán)失?。籪alse表示并非因為nonce值過期才鑒權(quán)失敗,。 返回: 無 調(diào)用此方法,,置參數(shù)stale為true,即向客戶端發(fā)揮“過期”質(zhì)詢,。HttpDigestAuth中還有其他幾種形式的setUnau方法,,凡是未指定stale的,,均默認(rèn)為false。 無論哪種參數(shù)形式的setUnauth方法,,都必須每次生成一個新的nonce值,。生成nonce的算法RFC2617并沒有做規(guī)定,HttpDigestAuth類在generateNonce方法中生成nonce,,算法為 NOnce = BCD( MD5( <client-IP>:<time-stamp>:<private-key> ) ) 即:取得客戶端IP地址、當(dāng)前時間(毫秒數(shù)),,以及一個私有的key,,將他們用冒號連接起來,取其MD5摘要,,然后將所得的字節(jié)數(shù)組轉(zhuǎn)換為十六進(jìn)制字符串(即BCD碼),。
五、應(yīng)用舉例
這里通過一個例子來說明如何使用Web平臺提供的API實現(xiàn)HTTP認(rèn)證,。 新建一個JSP文件AuthTest.jsp,,源代碼如下(讀者可以將表格的右邊一列拷貝到一個文本文件中,另存為AuthTest.jsp即可),。將此文件部署到一個應(yīng)用服務(wù)器中(如Tomcat等),,啟動服務(wù)器后,用IE請求這個JSP,,即可看到瀏覽器彈出對話框提示輸入用戶名和密碼,。 驗證時,可以在IE中嘗試輸入正確或錯誤的用戶名,、密碼,,并可在對話框中選擇“取消”,看看效果如何,。注意如果輸入了正確的用戶名和密碼,,認(rèn)證通過之后,IE不會再彈出對話框,,除非重新啟動一個IE窗口,,或者修改JSP文件中的用戶名、密碼字符串導(dǎo)致認(rèn)證失敗,。 第11行生成HttpAuth接口的一個實例,,即一個HttpBasicAuth對象,用以測試基本認(rèn)證,。第12行取得用戶名,,然后判斷用戶名是否是“abc”,若不是,,則在第15行設(shè)置未認(rèn)證響應(yīng),,指定realm值為“BasicAuthUser”,,客戶端的對話框里會看到這個字符串。如果用戶名正確,,第17行調(diào)用authenticate方法判斷用戶名/密碼是否為“abc”/“abcd”,,若不是,在第19行再設(shè)置未認(rèn)證響應(yīng),,這次指定realm為“BasicAuthPwd”(這里兩次指定不同的realm值只是為了做驗證,,實際應(yīng)用中一般應(yīng)該是一個相同的值)。若認(rèn)證通過,,則第22行在頁面上輸出一個字符串表明已認(rèn)證,。最后不管是哪種情況,都在第51行給客戶端返回它自己提交的認(rèn)證信息,。 從第25行到第49行是測試HTTP摘要認(rèn)證,,這里被注釋掉了,驗證時可將其恢復(fù),,同時注釋掉第11到23行測試基本認(rèn)證的部分,。與基本認(rèn)證相似,首先仍然是生成一個HttpAuth接口的實例,,但這次是HttpDigestAuth對象,;然后判斷用戶名和密碼是否正確。如果都正確,,在第38行取出nc值,,判斷其是否超過上限10,若超過則在第45行設(shè)置認(rèn)證未通過(nonce過期)響應(yīng),。
七,、小結(jié)
本文介紹了HTTP認(rèn)證的概念、規(guī)范以及ZX Web平臺提供的API,。RFC2617中還涉及到使用代理服務(wù)器時HTTP認(rèn)證的一些規(guī)范,,這里就不作介紹了。 HTTP認(rèn)證雖然不是安全的認(rèn)證方式,,但仍不失為一種簡單易用的安全措施,,在許多地方被采用。項目開發(fā)中,,在決定是否采用HTTP認(rèn)證時,,一定要考慮方案的安全性,以及項目本身對安全性的要求,;另一方面還要考慮瀏覽器對HTTP認(rèn)證的支持,。RFC2617發(fā)布時,一般的瀏覽器都支持基本認(rèn)證,,而只有微軟的IE支持摘要認(rèn)證,。兩個y6
|