關於WSSE驗證-- 一種驗證用戶的方法

大家通常驗證用戶做法:
1. BASIC驗證模式: 把用戶名和密碼採用Base64編碼之後,放在HTTP HEADER裏,發到服務器的。
2. FORM驗證模式: 就什麼都不處理,直接發到服務器。
3. 還有其他證書驗證,摘要驗證等,這些不在這篇文章討論範圍。

由於是明文傳輸,密碼很容易被截獲,從而造成密碼的丟失。今天和老大討論RESTful的模式時,想到了認證的問題,因爲REST提倡無狀態,我們老大提到了WSSE的問題,於是我就搜索一下。

密碼傳輸的問題通常是用HTTPS來解決,當然這個很完美,但有些限制。有些情況下不能用HTTPS來解決,例如多個應用使用一個單獨IP地址來訪問時,由於服務器證書裏的信息和域名是必須匹配的,所以一個應用使用了HTTPS, 而另一個就不能用了。還有一個辦法就是用摘要驗證,當然也可以解決這個問題,但是需要在服務器上配置相應的功能模塊。如果服務器不可控(例如臨時借用別人的服務器)也沒有辦法做到。

而WSSE的驗證模式可以解決以上問題。不需在服務器做額外配置。具體過程如下:
1. 開始於兩個信息: 用戶名和密碼。
2. 創建一個隨機的nonce(不知道應該譯成什麼,反正就是隨機的一個只能用一次的字符串),這個產生算法要夠強健,不能讓人猜出下一個產生的是什麼。
3.創建一個"產生時間戳", 並轉換成W3DTF格式
4.創建一個密碼摘要:
PasswordDigest = Base64 \ (SHA1 (Nonce + CreationTimestamp + Password))

舉例說明:
1.用戶發一個請求:
POST /atom.cgi HTTP/1.1
Host: bob.example.com
Content-Type: application/atom+xml

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://purl.org/atom/ns#">
<title>My Entry Title</title>
<created>2003-12-15T14:43:07Z</created>
<content type="application/xhtml+xml" xml:lang="en">
<div xmlns="http://www.w3.org/1999/xhtml">
<p>Hello, <em>weblog</em> world!</p>
<p>This is my third post <strong>ever</strong>!</p>
</div>
</content>
</entry>

2. 由於沒有驗證信息,服務器以401來響應:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: WSSE realm="foo", profile="UsernameToken"


[b]注:[/b][color=blue]還有的文章講這裏服務器生成一個nonce, 然後在下一步附加到Request裏,一塊參與摘要生成。這個server nonce本身好像沒有什麼用途,但由於客戶端nonce沒有生成規則和長度限制(甚至如果服務器不保存以前使用過的,都無法判斷是不是每次都一樣的),而生成一個server nonce參與生成摘要可以保證摘要的變化性,就是每次都不一致。由於這個nonce是臨時生成,一次有效,中間被人截獲也無所謂。在驗證時,由於是摘要驗證,服務器必須保存這個nonce到驗證結束,然後再及時清除。不過加了server nonce的限制,必然會使訪問服務的客戶端訪問兩次服務器才能真正訪問服務,就是不能直接把身份信息附加上,直接訪問服務。感覺這個就是標準的摘要驗證差不多了,就變成了"請求-響應"模式了。[/color]

3. 用戶輸入用戶名和密碼,並且生成摘要,以UserToken形式發送到服務器:
POST /atom.cgi HTTP/1.1
Host: bob.example.com
Content-Type: application/atom+xml
Authorization: WSSE profile="UsernameToken"
X-WSSE: UsernameToken Username="bob", PasswordDigest="quR/EWLAV4xLf9Zqyw4pDmfV9OY=", Nonce="d36e316282959a9ed4c89851497a717f", Created="2003-12-15T14:43:07Z"

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://purl.org/atom/ns#">
<title>My Entry Title</title>
<created>2003-12-15T14:43:07Z</created>
<content type="application/xhtml+xml" xml:lang="en">
<div xmlns="http://www.w3.org/1999/xhtml">
<p>Hello, <em>weblog</em> world!</p>
<p>This is my third post <strong>ever</strong>!</p>
</div>
</content>
</entry>

4.服務器通過時間戳和Nonce以及服務器保存的密碼進行生成摘要,如果通過驗證就可以允許用戶訪問資源。

這樣一個過程,我覺得能解決一些問題,但是還有一些疑問:
1.由於客戶要生成摘要和client nonce,客戶端必須具有生成它們的能力,或者瀏覽器支持這種協議。
[color=darkblue]現在客戶端的能力都比較強大,javascript就可以實現摘要的生成。具體程序參考:[/color][url]http://pajhome.org.uk/crypt/md5/[/url],目前爲止好像不沒有哪個瀏覽器支持這種協議的。

2.由於只發送摘要,並沒有真正發送密碼,解決中間攻擊的擔憂。
[color=darkblue]這個不錯,就要的這種效果。[/color]

3.由於nonce是隻用一次,下次就隨機產生另一個,由於這個是在客戶端產生的,如果產生暴力猜測密碼的情況怎麼辦?
[color=darkblue]這裏的nonce只用一次就失效,可以防止黑客的replay攻擊。但這過程中沒有防止暴力攻擊,不過有一個時間戳應該可以利用,如在服務器判斷3或者1,2秒之內不能重試登錄, 這個雖然不能完全避免,但至少可以減少一些攻擊次數。其實最好的解決辦法就是強口令,一個強口令就把這個問題解決的比較徹底了。如果不能強制用戶使用強口令的話,我們可以加入通常採用的驗證碼的機制。還有就是上面提到的server nonce應該也可以直到一些作用。[/color]

4.如果服務器不保存真正的密碼,而是隻保存摘要的話,那用這種方法豈不是不能驗證用戶的合法性了?
[color=darkblue]如果服務器不保存真正的密碼,而是摘要。如LDAP裏一般就不保存明文密碼,一般數據庫裏也不會保存真正明文密碼,這個問題我還真想不到什麼辦法。如果服務器的摘要算法和客戶端完全一致的話,可以用以下方法生成客戶端摘要:[/color]
[b]PasswordDigest = Base64 \ (SHA1 (Nonce + CreationTimestamp + [/b][color=red]DIGEST[/color][b](Password)))[/b]。
[color=darkblue]就是把Password生成摘要,然後再用組合生成新的摘要。這樣在服務器端也能順利的驗證用戶的合法性。[/color]

我覺得這個方法可以和其他方法結合使用,應該效果不錯。至少多了一層防護。

本文的思想主要來自:[url]http://www.xml.com/pub/a/2003/12/17/dive.html[/url], 也引用他的測試的HTTP數據。加上我自己的理解。

如有不妥,希望能夠得到指正。最後感謝“Atom Authentication”文章作者Mark。但是這裏面的Atom和WSSE有什麼關係,並沒有搞清楚,可能Atom只是WSSE的一種實現? 望知道的哥們姐妹給一些提示。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章