最近有朋友在使勁研究如何不使用 HTTPS 的情況下保護使用者密碼安全。暫且不說研究過程,但結論是要保障安全必須後端參與,使用非對稱加密演算法 —— 如此一來,不如直接用 HTTPS 更簡單便捷有保障。使用免費 SSL 證書,一年一換,運維稍微麻煩一點,訪問稍微慢一點(證書認證過程好像會慢一些),但至少是專業的,比自己研究的沒經過大量檢驗的演算法靠譜多了。
假設已經做過必要的安全防範,目前唯一需要解決的問題是保障使用者密碼在 HTTP 的明文傳輸過程中不被竊取。為達此目的,研究過程如下:
對稱演算法加密密碼
如果說,密碼在明文傳輸過程中存在風險,最直接的解決辦法是讓密碼不再是原來的樣子。使用一個 KEY
來加密密碼,再將加密後的結果傳輸到後臺,由後臺解密使用。
看起來確實起作用,如果竊取到加密後的密碼資料,沒有 KEY
是不能解密出來的。然而,KEY
要用於前端加密,就一定會存在於前端的某個地方,而前端的所有資源都是使用者可以獲取並分析的,甚至可以使用瀏覽器的開發者工具通過各種除錯手段來分析獲取。所以 KEY
本身並不能安全儲存,安全性被打破。
此外,如果 secure_data
在傳輸過程被竊取,是可以重複使用的(因為 KEY
不變,加密結果就不會變),這也是一個不安全因素。
結論 ?使用對稱加密演算法來加密密碼的方式不能保證金鑰安全,也不能保證使用者密碼安全,更不能保護註冊/登入過程安全。
對密碼進行單純的 HASH
使用對稱加密,在密文和金鑰都被竊取的時候,可以解出密碼原文,對使用者在其他系統中(可能使用了相同密碼)造成威脅。為此,我們可以使用不可逆的 HASH 演算法來處理密碼,只要前後端計算方式相同,不需要解密出密碼原文,直接使用 HASH 結果就行。
使用者註冊時,假設我們有原密碼 my_password
,使用 MD5 計算後是 a865a7e0ddbf35fa6f6a232e0893bea4
。這個 HASH 送到後端之後,後端是不能從 HASH 反算出原密碼的,所以後端只能直接儲存這個 HASH。
那麼驗證的時候,同樣將原密碼計算成 HASH a865a7e0ddbf35fa6f6a232e0893bea4
,傳輸到後端,後端拿它和儲存的資料進行對比,相同則驗證通過。
有問題嗎?當然有,你看 ——
使用 Hash 的過程
不使用 HASH 的過程
從傳輸過程開始,Hash 前的過程和 Hash 後的過程並沒有區別。對於偷竊者來說,無所謂是拿到的 my_password
還是 a865a7...
,只要把它送到後端,就能成功登入。
所以這種做法並不能保護使用者登入!
不過密碼是採用 Hash 計算過的,是不是能保護使用者密碼不會竊取用於嘗試登入其他系統呢?也不能!
一些常見的密碼,比如 123456
採用 MD5 計算後是 e10adc3949ba59abbe56e057f20f883e
,拿這個 Hash 上百度就能查出來原密碼是 123456
。當然,專業人士會有專業的工具,也就是彩虹表(什麼是彩虹表?查查唄!),數以 TB 計的資料,可能大部分密碼都查得出來吧。
結論 ?從前端對密碼進行單純的 HASH 演算法,起不到任何保護作用。
其實從後端對密碼進行單純的 HASH 加密也只能起到很弱的保護作用,幾近裸奔。
引入金鑰,使用 HMAC
HMAC 比單純的 HASH 演算法,要多一個金鑰因素,可以認為是加密的 HASH。使用 HMAC 可以有效的阻礙彩虹表破解。因此使用 HMAC 保護使用者密碼原文。但是,如果傳輸的是 HMAC 計算結果,和前面的討論同理,並不能保護登入。
但使用 HMAC 和單純的 HASH 又有那麼一點不同。假設我們已經通過其他方式進行安全的註冊,後端已經儲存了使用者密碼 my_password
,來看看下面這個登入過程:
攻擊者只需要竊取 hash_result
即可仿冒登入,而 hmac_result
在傳輸過程中可以輕鬆竊取。
此外,還有一個不安全點:固化在指令碼中的 secure_key
是可以從拉到客戶的指令碼資源中分析出來的。
結論 ?使用固定 secure key 的 HMAC 演算法並不能保障登入安全,只能起到保密使用者密碼原文的作用。但是,由於
secure_key
很容易被拿到,所以對密碼原文的保護也相對較弱。
引入動態安全碼 (secure key)
繼續尋找解決辦法,我們可以想到一個改進方案:如果滿足下面兩個條件,上面遇到的問題似乎就能解決:
-
secure_key
並不是固定在指令碼中,而是動態產生的,就可以解決從指令碼分析獲得secure_key
的問題; - 這個
secure_key
使用 1 次後立即失效,那麼hmac_result
就不能用於再一次登入,可以解決hmac_result
被竊取複用的問題。
動態 secure_key
看起來是個不錯的辦法,但是它應該在哪裡動態產生呢?
如果在前端動態產生,就必須通知後端,傳輸的資料需要包含 secure_key
和 hmac_result
兩部分。他們都能被竊取使用。這種狀態下如果要保證 secure_key
只能使用一次,就必須要後端快取所有用過的 secure_key
備查 —— 顯然這會極大的增加後端負擔。因此,不可以由前端來產生動態安全碼。
用後端產生動態安全碼之後可以立即快取起來,同時傳輸給前端(這個過程可以前端發起請求)。前端按上述步驟對使用者密碼進行加密,將資料送回後端。後端檢查 secure_key
在快取中,取出來計算用於驗證的 hmac_result
,同時從快取中刪除 secure_key
;如果 secure_key
不在快取中,直接拒絕驗證。
引入動態 secure_key
解決了登入期間驗證使用者密碼的問題。
雖然資料在傳輸過程中仍然能被竊取,但是被竊取的資料不能重複使用,也不易破解出密碼原文(可花長時間暴力破解),所以這個方案是初步可行的 —— 但要注意,它可能會受中到中間人攻擊。
此外,該方案是不完整的,因為它只能解決驗證密碼(登入)的問題,不能解決儲存密碼(註冊)的問題 —— 注意到後端是直接從資料庫載入的 my_password
,這意味著在註冊時需要傳入密碼原文或者可解密出密碼原文的密文 —— 這在之前的研究中都還沒找到辦法。
結論 ?後端產生一次性使用的動態
secure_key
可以保障登入安全,但不能解決註冊安全。而且該方案需要注意防護中間人攻擊。注意:需要後端參與
使用非對稱加密
到目前為止,我們總算找到一個前端登入勉強可用的安全方案。接下來還要研究使用者註冊時該怎麼辦。
註冊時需要傳輸可解密的密文,而且在前端不能找到可用於解密的金鑰 —— 這個場景非常符合非對稱加密的應用場景 —— 前端使用公鑰加密,後端使用私鑰解密,問題就能得到完美解決:
結論 ?使用非對稱加密演算法,可以保護使用者密碼安全,也可以保護登入過程安全。但是要注意中間人攻擊。
注意:需要後端參與
使用 HTTPS
到此為止,我們已經非常接近 HTTPS 了。HTTPS 主要就是採用的非對稱加密演算法來保證資料傳輸安全。為了防止中間人攻擊還引入了公信機構(受信任的證書中心)。但是,這個安全過程的參與方,包括 HTTPS 使用的安全協議(SSL 和 TSL 等)、伺服器、公信機構、使用者自己等,都可能成為整個安全過程的短板。
作為專業保障 HTTP 傳輸安全的 HTTPS 協議都隨時在曝漏洞,請問,你又哪裡來的信心憑一已之力用自己的安全演算法實現來代替 HTTPS?如果沒有足夠的能力,還是安心用 HTTPS 吧!
請關注公眾號邊城客棧⇗
看完了先別走,點個贊 ⇓ 啊,讚賞 ⇘ 就更好啦!