當我們在談論前端加密時,我們在談些什麼
當然在談安全。前端安全是Web安全的一部分,常見的安全問題會有XSS、CSRF、SQL注入等,然而這些已經在程師界得到了相當高的重視並且有了很成熟的解決方案。 所以我們今天只談前端「加密」,一個部分人認為沒有意義的工作。 有爭議的事情總是那麼因崔斯汀,接下來就讓我們談談前端傳輸中的數據「加密」 。
前端傳輸的數據我們應該用什麼演算法加密,如何組織整個加密過程呢? 一般有幾種做法:
- JavaScript 加密後傳輸
- 瀏覽器插件內進行加密傳輸
- Https 傳輸
在這裡其實 HTTPS 是終極解決方案,所以文章對於仍在使用 HTTP 建站的小夥伴更有幫助。本文中將討論第一種方法,也就是使用 JavaScript "加密"。使用雙引號是因為這裡面分為兩種手段,一是使用數據摘要進行消息雜湊,二是使用非對稱加密演算法對明文進行加密。嚴格意義來說第一種手段並非加密,而是一種信息摘要的應用,為了闡述方便下文統統使用加密一詞。在進行下文之前,需要簡單的介紹幾個概念:
哈希與加密
上圖中我們可以明顯看到哈希和加密是兩個不同的東西,主要有兩點不同:
? 哈希演算法通常用於數據摘要,生成相同長度的文本。而加密演算法生成的密文長度與明文長度有關。
? 哈希演算法是不可逆的,而加密演算法是可逆的。
在加密演算法中又分為對稱加密(symmetric encryption)和非對稱加密(asymmetric encryption)。非對稱加密演算法中,加密密鑰和解密密鑰是不同的,分為私鑰和公鑰,我們熟知的 RSA 就是一種非對稱加密演算法。而對稱加密中,加密和解密都是用同一個密鑰,如 AES / DES。從性能上來說,非對稱加密相對於對稱加密要慢很多。在 HTTPS 中,認證過程使用了非對稱加密演算法,非認證過程中使用了對稱加密演算法。
前端加密的意義
在 HTTP 協議下,數據是明文傳輸,傳輸過程中網路嗅探可直接獲取其中的數據。 如用戶的密碼和信用卡相關的資料,一旦被中間人獲取,會給用戶帶來極大的安全隱患。另一方面在非加密的傳輸過程中,攻擊者可更改數據或插入惡意的代碼等。HTTPS 的誕生就是為了解決中間人攻擊的問題,但如今 HTTPS 的使用情況在國內並不樂觀,基本是因為成本或者性能的考量。那麼問題來了,如果仍然使用 HTTP 協議怎麼樣一定程度保證用戶的數據安全? 前端加密,即在數據發送前將數據進行哈希或使用公鑰加密。如果數據被中間人獲取,拿到的則不再是明文。
設想在如今公共 WIFI 流行的今天,一旦某一個 AP 的流量被攻擊者劫持,如果密碼和用戶名都是明文,那麼攻擊者將輕而易舉的使用這些資料進行 Replay 登錄。更糟糕的是很多用戶在不同的網站使用相同的賬號信息,用戶的隱私蕩然無存。作為網站服務提供者,網站設計和開發人員應該為用戶提供相對安全的服務,這是一種責任也是一種情懷。
另外如果前端使用哈希演算法,不僅可以幫助後端分擔部分計算壓力,還可以增加撞庫成本。具體的討論將在下文繼續。
如何加密
前面說過使用 JavaScript 加密有兩種方式,一是使用哈希進行信息摘要,一種是使用非對稱加密。在國內的知名站點中,這兩種方式都有人使用。接下來我們分別來深入了解加密過程,探討下如何應對不同的場景。
哈希「加密」
為了避免傳輸過程中的明文風險,前端可以在發送敏感數據前對其加密,如密碼。由於哈希演算法的不可逆性,中間人無法從截取的數據中得知用戶的密碼信息。但是這裡有一個問題,攻擊者仍然可以使用拿到的哈希值進行直接登錄。使用前端加密如何避免中間人重放攻擊?帶著這個問題,我們回顧下後台如何驗證用戶。
很多站點後台對於用戶密碼處理也是用哈希演算法,一方面因為不需要將密文解密成明文來比對密碼,另一方面是一旦加密演算法和密鑰泄露,那麼整個用戶資料庫就相當於明文存儲了。如果前端傳過來的是明文,那麼在註冊時將其哈希,存入資料庫。登錄時,將密碼哈希和資料庫對應的數據比對,若一致則說明密碼正確。
如果我們使用 Salt 是否可以解決問題?如果前端傳過來的是加了 salt 的哈希值,我們需要後端存有同一份 salt ,用其和資料庫的加密密碼哈希,然後與前端傳過來的哈希值比對。兩個過程的對比見下圖:
很容易知道,如果 Salt 不是每次登陸不同,那麼攻擊者仍可以使用加密後的密碼進行直接登陸,所以必須使用動態 Salt。動態 Salt 有很多方法,可以是動態的 Token,也可以直接使用現成的驗證碼。 這當中使用驗證碼是對後台系統改動較小且效果不錯的,我們來看看怎麼樣結合驗證碼進行前端加密。
前端先將密碼哈希,然後和用戶輸入的驗證碼進行哈希,得到的結果作為密碼欄位發送給伺服器。伺服器先確認驗證碼正確,然後再進行密碼驗證,否則直接返回驗證碼錯誤信息。
這種實踐保證了密碼在傳輸過程中的資料安全,即使攻擊者拿到了數據也無法重放。圖形化驗證碼更是增加了難度。另一方面該實踐大大增加了撞庫的成本。
前端加密一定程度保障了傳輸過程中的資料安全,那麼會不會有對兩端(客戶端和伺服器)有安全幫助呢?有幫助,使用一些前端加密手段,可以加大拖庫後的數據破解的難度。但是之前介紹的驗證碼方法不具有這樣的功能,因為資料庫存的仍是明文密碼哈希後的結果,那麼攻擊者可以繞過前端加密,可以直接暴力破解。
使用怎麼樣的前端加密方法可以增大拖庫後的數據破解難度?從兩個方面分別去思考,一是降低破解速度,二是需要前端加密結果影響資料庫的數據存儲。數據被暴力破解出來的時間與哈希演算法速度負相關。我們知道暴力破解即是使用相同的演算法把常用的字元組跑一遍,如果結果相同那麼就猜中明文了。如果演算法越快,便能更快的破解。例如 MD5 加密一次耗費1微秒,那麼攻擊者一秒鐘就可以猜大約 100萬個片語。所以為了減慢破解速度,我們需要增加破解的時間,一個直接的方法就是使用更慢的演算法,我們可以把常用的MD5演算法替換為 bcrypt,PBKDF2 等慢演算法。
對於需要前端加密結果影響資料庫的數據存儲,即為後端加密把前端加密結果當做明文密碼,那麼存在庫里的密碼便是前端哈希加上後端加密的結果。由於慢的演算法會增加伺服器計算壓力,當我們把一部分哈希工作拿到前端,即減慢了加密速度,減輕了伺服器壓力,也順帶完成了前端加密的工作。
加密速度減慢一定程度會降低用戶體驗,這也是一部分站點未啟用 https 的原因之一。但是因為我們的前端加密只會用在不常使用的登錄和註冊上,所以不會影響網站整體的體驗。
綜上,我們可以使用更慢的演算法,加之視前端哈希結果為明文密碼,便可增加拖庫破解的成本。既然增加了破解的成本,撞庫的成本也同樣增加了。為了避免攻擊者先前使用前後端加密生成的新字典,我們需要加鹽,並不定期更新鹽值。下圖文是使用過期 Salt 的方法描述:
前端加密使用定期刷新的 Salt,哈希後生成密文,再經過後端加密後即為用於比對的密文。資料庫中的密碼哈希值跟著 Salt 進行變化,前後端需要有一套salt的更新機制。
回顧前端哈希的方法,解決了中間人攻擊重放、加大了拖庫破解的難度。雖然這些方法都不能完全保證用戶安全,但是作為站點服務者,應該視用戶為上帝嘛。這些方法也不過就是幾行代碼的事,但是卻能一定程度的避免了用戶賬號被盜等風險。非對稱加密
上文中我們討論了前端的哈希加密以及應用的場景。對於十足的安全控來說,這樣的措施對於有心的攻擊者基本沒有用。那麼我們可以採用更強的措施保障傳輸中的敏感數據安全。
之前有說過加密演算法分為對稱加密和非對稱加密,因為前端的透明性,使用JavaScript來進行對稱加密是不可能的了,那麼只剩下了非對稱加密這個選擇。經過筆者幾天的調研發現,某鵝某浪的部分登錄頁面採用非對稱加密對密碼加密。這些站點目前都還是使用 http 協議,這樣的安全措施一定程度保證了用戶的密碼安全。
前端使用非對稱加密原理很簡單,前後端共用一套加密解密演算法,前端使用公鑰對數據加密,後端使用私鑰將數據解密為明文。中間攻擊人拿到密文,如果沒有私鑰的話是沒辦法破解的。可能有人會指出加密演算法一旦被破解,這一套安全措施就沒用了。況且 JavaScript 的生成安全隨機數還是個問題,所以其實這是用不了 https 的權衡之計。在沒有 https 的情況下,使用 JavaScript 非對稱加密或者 插件加密通訊是比較好的替代方法,後者優於前者。
安全不只情懷
作為一個前端開發者,我們眼裡不應該只充斥各種框架,更不能被旁人一句沒有意義的話給擊退。任何解決方案都有其適用的範圍,世界上根本就沒有銀彈,安全問題亦是如此。所以根據實際的情況採取符合自己的安全解決方案很重要,即使辦法不是 100% 有效,也不能放棄那 1% 可以為用戶保障安全的機會。
推薦閱讀:
※極樂技術周報(第二十期)
※開發者和用戶之間的世界觀距離有多大?
※前端系列教學(入門篇) - 響應式設計(2)
※[譯]2017年,一個Node開發者的好習慣