參見:http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Password_functions
我意譯一下,大致就是以下內容:
4.0版本之前
1、伺服器發送隨機字串(scramble_buff)給用戶端.
2、用戶端把使用者純文字密碼加密一下,然後將hash加上伺服器的隨機字串加密一下變成新的scramble_buff。(參見sql/password.c:scramble()).
3、用戶端將加密後的scramble_buff值發給服務端.
4、伺服器將mysql.user.Password的值加上原始隨機字串進行加密.
5、伺服器比對加密後的hash值和服務端送過來的加密後的scramble_buff.
6、如果一樣,則驗證成功.
基本就是一個挑戰機制。但是注意一點:實質上真正意義上的密碼是純文字密碼的加密hash值; 如果有人知道了這個使用者的mysql.user.Password(而不用知道原始純文字密碼)他就能直接登入服務端.
4.1以後版本
4.1以後資料庫儲存的密碼是用SHA1加密的:SHA1(SHA1(password))
1、伺服器發送隨機字串(scramble)給用戶端.
2、用戶端作如下計算:
stage1_hash = SHA1(純文字密碼).
token = SHA1(scramble + SHA1(stage1_hash)) XOR stage1_hash
3、用戶端將token發送給服務端
4、服務端作如下計算:
stage1_hash = token XOR SHA1(scramble + mysql.user.Password)
5、服務端比對SHA1(stage1_hash)和mysql.user.Password,如果匹配,則認證正確。
注意:SHA1(A+B)意思是SHA1(A字串串連B字串).
這次沒上一個版本的缺陷了. 有了mysql.user.Password和scramble也不能獲得token。因為他沒法獲得stage1_hash。
但是如果這人有這使用者的 mysql.user.Password 及網路上截取的一次完整驗證資料, 他也能根據這次截獲的token和scramble反解出stage1_hash的值。而由於stage1_hash是不變的,因此下次串連,他擷取了新的scramble後,自己加密一下token,送給服務端也能通過驗證而串連到伺服器.
最後放一個5.1的認證的抓包結果,注意標紅的地方:
server > 127.0.0.1.49130: Handshake
127.0.0.1.49130 > server: Handshake (new auth)
- server > 127.0.0.1.49130: Handshake <proto 10 ver 5.1.41-3ubuntu12.6 thd 55 scramble 1EGu9\Aq8_UnI_'@L<*Y >
- 127.0.0.1.49130 > server: Handshake (new auth) <user root db (null) token 6d2c7025c412b997788525b19a5167c89dafcbe max pkt 16777216>
- server > 127.0.0.1.49130: OK <fields 0 affected rows 0 insert id 0 warnings 0>