來源:藏色散人 發(fā)布時間:2020-04-28 11:59:13 閱讀量:999
1、密碼保護(hù)三原則
絕對不能知道用戶的密碼
我們絕對不能知道用戶的密碼,也不能有獲取用戶密碼的方式,如果應(yīng)用的數(shù)據(jù)庫被黑,你肯定不希望數(shù)據(jù)庫中有純文本或能解密的密碼。任何時候,知道的越少越安全。
絕對不要約束用戶的密碼
如果要求密碼符合特定的模式,其實是為不懷好意的人提供了攻擊應(yīng)用的途徑,如果必須約束密碼,我建議只限制最小長度,把常用的密碼或基于字典創(chuàng)建的密碼加入黑名單也是好主意。
絕對不能通過電子郵件發(fā)送用戶密碼
如果你通過電子郵件給用戶發(fā)送密碼,用戶會知道三件事:你知道他的密碼,你使用純文本或能解密的方式存儲了他的密碼,你沒有對通過互聯(lián)網(wǎng)發(fā)送純文本的密碼感到不安。
我們應(yīng)該在電子郵件中發(fā)送用于設(shè)置或修改密碼的URL,Web應(yīng)用通常會生成一個唯一的令牌,這個令牌只在設(shè)定或修改密碼時使用一次(比如修改密碼),通常我們把這個令牌作為設(shè)置或修改密碼URL的一個參數(shù),當(dāng)用戶訪問這個URL時,應(yīng)用會驗證令牌是否有效,如果有效則繼續(xù)操作,操作完成后,令牌失效,不能重復(fù)使用。
2、密碼存儲算法
關(guān)于密碼存儲的最佳實踐是計算密碼的哈希值,而不是加密用戶的密碼。加密和哈希不是一回事,加密事雙向算法,加密的數(shù)據(jù)可以解密,而哈希是單向算法,哈希后的數(shù)據(jù)不能再還原成原始值,而且相同的數(shù)據(jù)得到的哈希值始終相同。
在數(shù)據(jù)庫中存儲用戶的密碼,要先計算密碼的哈希值,然后在數(shù)據(jù)庫中存儲密碼的哈希值,如果黑客攻入數(shù)據(jù)庫,只能看到無意義的密碼哈希值,需要花費(fèi)大量的時間和NSA資源才能破解。
哈希算法有很多種(如md5、SHA1、bcrypt和scrypt),有些算法速度很快,用于驗證數(shù)據(jù)完整性;有些算法的速度則很慢,旨在提高安全性。生成密碼和存儲密碼時要使用速度慢、安全性高的算法。
目前,最安全的算法當(dāng)屬bcrypt,與md5和SHA1不同,bcrypt故意設(shè)計得很慢,bcrypt會自動加鹽(salt),防止?jié)撛诘牟屎绫砉簦琤crypt算法會花費(fèi)大量時間反復(fù)處理數(shù)據(jù),生成特別安全的哈希值。在這個過程中,處理數(shù)據(jù)的次數(shù)叫工作因子,工作因子的值越高,破解密碼所需的時間越長,安全性越好。bcrypt算法永不過時,如果計算機(jī)運(yùn)算速度變快了,我們只需提高工作因子的值。
3、密碼哈希API
通過前面的介紹,我們知道在處理用戶的密碼時要考慮很多東西,好在PHP 5.5.0原生的哈希API(http://php.net/manual/zh/book.password.php)提供了很多易于使用的函數(shù),大大簡化了計算密碼哈希值和驗證密碼的操作,而且,這個密碼哈希API默認(rèn)使用bcrpt算法。
開發(fā)Web應(yīng)用時,有兩個地方會用到密碼哈希API:注冊用戶和用戶登錄,下面我們以Laravel提供的用戶注冊和登錄為例,看看PHP密碼哈希API時如何簡化這兩個操作的。
注:Laraval框架內(nèi)置的用戶注冊和登錄功能正是使用了PHP哈希API實現(xiàn)密碼的存儲和驗證。
注冊用戶
用戶注冊在AuthController中完成,新用戶的創(chuàng)建在該控制器的create方法中實現(xiàn):
可以看到這里使用了Laravel提供的輔助函數(shù)bcrypt對用戶提交的密碼進(jìn)行哈希并保存到數(shù)據(jù)庫。bcrypt函數(shù)定義如下:
這里我們可以看出實際上是調(diào)用了別名為hash的服務(wù)提供者實例上的make方法實現(xiàn)哈希密碼,進(jìn)入HashServiceProvider,在register方法中我們可以看到hash對應(yīng)的類為BcryptHasher,在該類中我們找到了make方法:
這里的核心是調(diào)用了PHP提供的password_hash函數(shù),該函數(shù)接收三個參數(shù),第一個是用戶輸入的密碼值,第二個參數(shù)是使用的哈希算法(更多算法查看:http://php.net/manual/zh/password.constants.php),第三個參數(shù)可選,包括salt和cost兩個選項,分別表示干擾字符串(加鹽)和前面提到的工作因子,工作因子可以隨著硬件性能的提升而提升,不傳的話使用隨機(jī)加鹽和默認(rèn)工作因子(計算哈希值一般需要0.1~0.5s)。如果計算失敗,拋出異常。
用戶登錄
在Larval中以在auth.php中使用session作為guards、eloquent作為providers實現(xiàn)用戶登錄認(rèn)證為例(實際上默認(rèn)設(shè)置就是這樣),登錄驗證最終會走到EloquentUserProvider的validateCredentials方法:
$this->hasher對應(yīng)的實現(xiàn)也是BcryptHasher類,我們來查看它的check方法:
其中傳入的第一個參數(shù)是用戶輸入的密碼,第二個參數(shù)是用戶注冊時保存的密碼哈希值,如果哈希值為空直接返回false,否則調(diào)用php提供的password_verify函數(shù),該函數(shù)用于驗證密碼(純文本)和哈希值是否匹配,匹配返回true,否則返回false。
重新計算哈希值
通過上述步驟用戶已經(jīng)可以實現(xiàn)登錄認(rèn)證了,但是登錄前我們還需要檢查現(xiàn)有的密碼哈希值是否已經(jīng)過期,如果過期,需要重新計算密碼哈希值。
為什么要重新計算呢?加入我們的應(yīng)用創(chuàng)建于兩年前,那時候使用的工作因子時10,現(xiàn)在使用的是20,因為計算機(jī)的速度更快了,黑客也更聰明了。可以有些用戶的密碼哈希值仍然是工作因子為10時生成的,這時,登錄認(rèn)證通過后,要使用password_needs_refresh函數(shù)檢查用戶記錄中現(xiàn)有的哈希值是否需要更新,這個函數(shù)可以確保指定的密碼哈希值是使用最新的哈希算法創(chuàng)建的。如果確實需要重新計算生成密碼的哈希值,要使用make方法生成新的哈希值并更新數(shù)據(jù)庫中的原密碼。
Laraval中目前并沒有使用這一功能,但是在BcryptHasher類中已經(jīng)提供了對應(yīng)的函數(shù):