你知道什麼事情最捉急麼?慢的網站。
不幸的是,驗證過程中某些特定部分就應該是緩慢的。這似乎違反直覺,但是驗證過程中的慢速是保證安全的很大一部分。
本文介紹 Python 中的驗證過程(不僅僅是雜湊法),還有如何在不降低安全性的條件下提高 Python 的驗證速度。
我將帶你讀一下 Python 虛擬碼,並教你如何保證驗證系統儘可能快而需要掌握的知識。
密碼雜湊法很慢
當使用者在網站上註冊時,給了密碼——最好的方法是在儲存之前去雜湊使用者密碼。這意味著你可以用一個雜湊演算法,比如 bcrypt 或 scrypt 這樣,可以把一個密碼字串翻譯為不能被反向破解的胡言亂語。
一旦用了雜湊法,就沒有辦法回覆原始密碼了。
有個重要的點:強大的雜湊函式,比如 bcrypt 和 scrypt ,是命中註定般的慢啊!
雜湊法需要更多的 CPU、RAM 和時間用來計算,這意味著一個攻擊者會需要花費更長的時間來暴力破解一個密碼。
現在,假設你已經黑入了資料庫,還有可以訪問全部使用者密碼的雜湊。
也可以說是有10個 bcrypt 雜湊。
如果想要找到密碼,你只能去:暴力破解它們。
想做到這點,你會寫一些程式碼來遍歷所有可能的密碼組合(在下面的例子中我用 PyPI 中的brute庫):
1 2 3 4 5 6 7 8 9 10 11 |
$ pip install brute # crack.py from brute import brute HASH_TO_CRACK = 'xxx' for pw in brute(length=8): if HASH_TO_CRACK == bcrypt(pw): print 'Password is:', pw break |
上面的例子中,我們會遍歷所有8個字母或以下的可能密碼,並試圖去暴力破解它。
每次新生成一個可能的密碼,通過 bcrypt
函式執行,得到雜湊,將它和你已經黑了的密碼雜湊比較。如果你得到了一對兒,意味著你已經成功暴力破解了使用者密碼!
但是有個重點:bcrypt 和 scrypt 會花很長時間去計算,佔用很多資源。
因為bcrypt和scrypt在數學意義上計算很慢,攻擊者會因為佔用太多計算機資源而很艱難地花費時間來暴力破解。
因此,我們現在理解了雜湊的原理,以及為什麼時間密集——我們來討論一下驗證。
注:如果你對於瞭解密碼安全有興趣,你也許會想要去讀篇寫了有點時間的文章對於密碼安全的正確方法——是個好的閱讀材料。如果你想更加瞭解,看看這個.
如何進行驗證工作
當用使用傳統的方法註冊或登入網站時,你需要雜湊他們的密碼,然後儲存到資料庫或與資料庫中的值進行比較——然後發生了什麼?你要記住,使用者不是有一個會話ID就是有某種API祕鑰。
看看這些虛擬碼:
1 2 3 4 5 6 |
# register.py user = User('r@rdegges.com', 'hithere!123') user.save() # save this user to the database # Create a new session cookie in the browser, which holds the user ID. session.create('session', user.id) |
這個辦法的思路是使用者 ID 儲存到使用者的瀏覽器快取中,下一次使用者請求你網站時,使用者瀏覽器就傳送快取中的使用者 ID 給你的伺服器,並允許你瀏覽使用者賬號下的資訊,而不需要再次輸入郵件地址或密碼。
再看看這些虛擬碼:
1 2 |
# views.py user = User.find(id=session) |
正如你料想的——通過 ID 查詢使用者賬號是非常快的(密碼不雜湊是必要的)。
因此——這就意味著初始建立賬戶和登入密碼的過程較慢——剩下的就都很快了!
我們繼續。
優化速度
一旦使用者資料請求網站的每個頁面,資料就要接收的非常頻繁。
如過你使用了 Postgres 或 MySQL 之類的資料庫,這就意味著你的網站如果有100個使用者,那你每秒要查詢資料庫的users表
數百次。
如果你的網站還需要做其他事,你可能忍不了這麼慢的網頁載入。
你可以做什麼來提速呢?快取!
快取是速度和效能問題的解決方案——提升訪問你網站的速度時,讓使用者資料快速載入速度的有效方法之一。
這種方法超簡單:儲存鍵值對到持久化儲存中,使用者ID作為鍵,使用者的賬戶資料作為雜湊值。
這很有用,因為下次使用者請求你網頁時,就可以傳送會話快取,而不是通過資料庫查詢賬戶,你也可以直接從記憶體資料庫快取查詢這些資訊。
對於快取,你也許更想要儲存資料到 memcached 或 redis 之類的快取系統中,令人開心的是,python都有對應的庫
在虛擬碼裡,你可以這麼做:
1 2 3 4 5 6 |
if session: user = cache.get(session) # If no user was found in the cache, try querying the database directly. if not user: user = User.get(id=session) |
實現
如果你用了 Django之類的web框架,那用(框架)內建的校驗系統就可以很容易的完成這篇文章提及的所有內容。
如果你用了另一個框架/工具,你也許想到google搜尋一下相關庫。不管你用什麼工具,本文的幾個典型選項都能幫到你。
最後,如果你在用 Python / Flask / Django,並且想要得到關於使用者儲存和安全最全、最好的實踐,你可以看看我們的開發者服務 Stormpath。
我們的服務為你儲存使用者賬戶和使用者資料,負責處理密碼雜湊、加密、資料安全、最好的實踐和其他的所有。
可以免費用於大多數應用,也有輕鬆整合到Python、Flask 和 Django 應用。
我們最新發布的 Python 庫中包含了內建的支援記憶體資料庫、memcached 和 redis 快取,以確保總是儘可能快速訪問你的網站。
如果你想用Stormpath,你可以從下面地址得到我們的庫: