PHP中常見(jiàn)的密碼處理方式和建議總結(jié)
前言
在使用PHP開(kāi)發(fā)Web應(yīng)用的中,很多的應(yīng)用都會(huì)要求用戶注冊(cè),而注冊(cè)的時(shí)候就需要我們對(duì)用戶的信息進(jìn)行處理了,最常見(jiàn)的莫過(guò)于就是郵箱和密碼了,本文意在討論對(duì)密碼的處理:也就是對(duì)密碼的加密處理。
密碼安全的重要性我們就不用再去強(qiáng)調(diào),隨著在線攻擊的增多,如果我們對(duì)密碼沒(méi)有進(jìn)行合適的處理或做防御措施,我們的應(yīng)用就會(huì)肯定會(huì)收到來(lái)自各方的威脅和攻擊。
所以作為開(kāi)發(fā)者,我們需要對(duì)用戶的密碼做好預(yù)防措施。
關(guān)于密碼我們應(yīng)該遵守的一些原則
絕對(duì)不能知道用戶的密碼
- 我們絕對(duì)不能知道用戶的密碼,也不能有獲取用戶密碼的方式。
- 知道的越少(包括我們開(kāi)發(fā)者自己)越安全。
絕對(duì)不去約束用戶的密碼
- 最好不要去約束密碼的長(zhǎng)度、格式等。
- 如果要求密碼符合一個(gè)特定的模式,其實(shí)對(duì)于那些不懷好意的人也提供了攻擊的途徑。
- 如果必須要約束的話,建議只限制最小長(zhǎng)度。并把常用的密碼或基于字典創(chuàng)建的密碼加入黑名單,也是一個(gè)好主意。
絕對(duì)不通過(guò)電子郵件發(fā)送用戶的密碼
對(duì)于一個(gè)web應(yīng)用來(lái)說(shuō),重置或修改密碼時(shí),我們應(yīng)該在郵件里發(fā)送用于設(shè)定或修改密碼的 URL 。而且這個(gè)URL中應(yīng)該會(huì)包含一個(gè)唯一的令牌,這個(gè)令牌只能在設(shè)定或修改密碼時(shí)使用一次。在設(shè)定或修改密碼之后,我們就應(yīng)該把這個(gè)令牌置為失效。
使用 bcrypt 計(jì)算用戶密碼的哈希值
目前,通過(guò)大量的審查,最安全的哈希算法是 bcrypt 。
首先,我們明確兩個(gè)概念,哈希、加密。哈希和加密有什么區(qū)別?
加密
加密是雙向算法,加密的數(shù)據(jù)之后通過(guò)解密還可以得到。
哈希
哈希是單向算法,哈希后的數(shù)據(jù)不能再還原成原始值。
哈希算法的用途,
驗(yàn)證數(shù)據(jù)的完整性(要求算法速度快)
- 用戶提高密碼等需要單向驗(yàn)證的數(shù)據(jù)的安全性(要求安全性高,甚至故意要求時(shí)間慢)
- 一般我們?cè)跀?shù)據(jù)庫(kù)中保存的應(yīng)該是計(jì)算出來(lái)的密碼的哈希值。這樣即使我們的數(shù)據(jù)庫(kù)泄露了,他們也只能看到這些無(wú)意義的密碼的哈希值。
哈希的算法有很多種,
MD5
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于確保信息傳輸完整一致。是計(jì)算機(jī)廣泛使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語(yǔ)言普遍已有MD5實(shí)現(xiàn)。將數(shù)據(jù)(如漢字)運(yùn)算為另一固定長(zhǎng)度值,是雜湊算法的基礎(chǔ)原理,MD5的前身有MD2、MD3和MD4。
SHA1
安全哈希算法(Secure Hash Algorithm)主要適用于數(shù)字簽名標(biāo)準(zhǔn) (Digital Signature Standard DSS)里面定義的數(shù)字簽名算法(Digital Signature Algorithm DSA)。對(duì)于長(zhǎng)度小于2^64位的消息,SHA1會(huì)產(chǎn)生一個(gè)160位的消息摘要。當(dāng)接收到消息的時(shí)候,這個(gè)消息摘要可以用來(lái)驗(yàn)證數(shù)據(jù)的完整性。在傳輸?shù)倪^(guò)程中,數(shù)據(jù)很可能會(huì)發(fā)生變化,那么這時(shí)候就會(huì)產(chǎn)生不同的消息摘要。 SHA1有如下特性:不可以從消息摘要中復(fù)原信息;兩個(gè)不同的消息不會(huì)產(chǎn)生同樣的消息摘要,(但會(huì)有1x10 ^ 48分之一的機(jī)率出現(xiàn)相同的消息摘要,一般使用時(shí)忽略)。
bcrypt
bcrypt是專門(mén)為密碼存儲(chǔ)而設(shè)計(jì)的算法,基于Blowfish加密算法變形而來(lái),由Niels Provos和David Mazières發(fā)表于1999年的USENIX?!crypt最大的好處是有一個(gè)參數(shù)(work factor),可用于調(diào)整計(jì)算強(qiáng)度,而且work factor是包括在輸出的摘要中的。隨著攻擊者計(jì)算能力的提高,使用者可以逐步增大work factor,而且不會(huì)影響已有用戶的登陸?!crypt經(jīng)過(guò)了很多安全專家的仔細(xì)分析,使用在以安全著稱的OpenBSD中,一般認(rèn)為它比PBKDF2更能承受隨著計(jì)算能力加強(qiáng)而帶來(lái)的風(fēng)險(xiǎn)。bcrypt也有廣泛的函數(shù)庫(kù)支持,因此我們建議使用這種方式存儲(chǔ)密碼。
scrypt
scrypt不僅計(jì)算所需時(shí)間長(zhǎng),而且占用的內(nèi)存也多,使得并行計(jì)算多個(gè)摘要異常困難,因此利用rainbow table進(jìn)行暴力攻擊更加困難。scrypt沒(méi)有在生產(chǎn)環(huán)境中大規(guī)模應(yīng)用,并且缺乏仔細(xì)的審察和廣泛的函數(shù)庫(kù)支持 。但是,scrypt在算法層面只要沒(méi)有破綻,它的安全性應(yīng)該高于PBKDF2和bcrypt。
目前,通過(guò)大量的審查,最安全的哈希算法是 bcrypt 。與 MD5 和 SHA1 不同, bcrypt 算法會(huì)自動(dòng)加鹽,來(lái)防止?jié)撛诘牟屎绫砉簟?bcrypt 算法會(huì)花費(fèi)大量的時(shí)間反復(fù)處理數(shù)據(jù),來(lái)生成安全的哈希值。在這個(gè)過(guò)程中,處理數(shù)據(jù)的次數(shù)叫工作因子(work factor)。工作因子的值越高,破解密碼哈希值的時(shí)間會(huì)成指數(shù)倍增長(zhǎng)。
bcrypt 算法永不過(guò)時(shí),如果計(jì)算機(jī)的運(yùn)算速度變快了,我們只需要提高工作因子即可。
順帶說(shuō)一下,任何情況下盡可能的不要使用 md5 算法,至少也要使用 SHA 系列的哈希算法。因?yàn)閙d5算法以目前計(jì)算機(jī)的計(jì)算能力來(lái)說(shuō)顯得比較簡(jiǎn)單,而 md5 的性能優(yōu)勢(shì)現(xiàn)在也已經(jīng)完全可以忽略不計(jì)了。
密碼哈希API
上面我們說(shuō)到 bcrypt 算法最安全,最適合對(duì)我們的密碼進(jìn)行哈希。 PHP 在 PHP5.5.0+ 的版本中提供了原生的密碼哈希API供我們使用,這個(gè)密碼哈希API默認(rèn)使用的就是 bcrypt 哈希算法,從而大大簡(jiǎn)化了我們計(jì)算密碼哈希值和驗(yàn)證密碼的操作。
PHP原生密碼哈希API
密碼哈希函數(shù):
- password_get_info
返回指定的哈希值的相關(guān)信息 - password_hash
創(chuàng)建密碼的哈希(hash) - password_needs_rehash
檢查給定的哈希是否與給定的選項(xiàng)匹配 - password_verify
驗(yàn)證密碼是否和哈希匹配
password_get_info
說(shuō)明
array password_get_info ( string $hash )
參數(shù)
hash, 一個(gè)由 password_hash() 創(chuàng)建的散列值。
示例,
<?php var_dump(password_get_info($hash)); // Example array(3) { ["algo"]=> int(1) ["algoName"]=> string(6) "bcrypt" ["options"]=> array(1) { ["cost"]=> int(10) } } ?>
password_hash
說(shuō)明
string password_hash ( string $password , integer $algo [, array $options ] )
password_hash() 使用足夠強(qiáng)度的單向散列算法創(chuàng)建密碼的哈希(hash)。 password_hash() 兼容 crypt()。 所以, crypt() 創(chuàng)建的密碼哈希也可用于 password_hash()。
當(dāng)前支持的算法:
- PASSWORD_DEFAULT
使用 bcrypt 算法 (PHP 5.5.0 默認(rèn))。 注意,該常量會(huì)隨著 PHP 加入更新更高強(qiáng)度的算法而改變。 所以,使用此常量生成結(jié)果的長(zhǎng)度將在未來(lái)有變化。 因此,數(shù)據(jù)庫(kù)里儲(chǔ)存結(jié)果的列可超過(guò)60個(gè)字符(最好是255個(gè)字符)。 - PASSWORD_BCRYPT
使用 CRYPT_BLOWFISH 算法創(chuàng)建哈希。 這會(huì)產(chǎn)生兼容使用 “$2y$“ 的 crypt()。 結(jié)果將會(huì)是 60 個(gè)字符的字符串, 或者在失敗時(shí)返回 FALSE。
支持的選項(xiàng):
- salt - 手動(dòng)提供哈希密碼的鹽值(salt)。這將避免自動(dòng)生成鹽值(salt)。
省略此值后,password_hash() 會(huì)為每個(gè)密碼哈希自動(dòng)生成隨機(jī)的鹽值。這種操作是有意的模式。
Warning 鹽值(salt)選項(xiàng)從 PHP 7.0.0 開(kāi)始被廢棄(deprecated)了。 現(xiàn)在最好選擇簡(jiǎn)單的使用默認(rèn)產(chǎn)生的鹽值。 - cost - 代表算法使用的 cost。crypt() 頁(yè)面上有 cost 值的例子。
省略時(shí),默認(rèn)值是 10。 這個(gè) cost 是個(gè)不錯(cuò)的底線,但也許可以根據(jù)自己硬件的情況,加大這個(gè)值。
參數(shù)
- password, 用戶的密碼。
使用 PASSWORD_BCRYPT 做算法,將使 password 參數(shù)最長(zhǎng)為72個(gè)字符,超過(guò)會(huì)被截?cái)唷?/li> - algo, 一個(gè)用來(lái)在散列密碼時(shí)指示算法的密碼算法常量。
- options, 一個(gè)包含有選項(xiàng)的關(guān)聯(lián)數(shù)組。目前支持兩個(gè)選項(xiàng):
salt,在散列密碼時(shí)加的鹽(干擾字符串),
cost,用來(lái)指明算法遞歸的層數(shù)。這兩個(gè)值的例子可在 crypt() 頁(yè)面找到。
省略后,將使用隨機(jī)鹽值與默認(rèn) cost。
示例
示例1,使用默認(rèn)算法哈希密碼
<?php /** * 我們想要使用默認(rèn)算法哈希密碼 * 當(dāng)前是 BCRYPT,并會(huì)產(chǎn)生 60 個(gè)字符的結(jié)果。 * * 請(qǐng)注意,隨時(shí)間推移,默認(rèn)算法可能會(huì)有變化, * 所以需要儲(chǔ)存的空間能夠超過(guò) 60 字(255字不錯(cuò)) */ echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT)."\n"; ?> // 輸出類似于: // $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
示例2,手動(dòng)設(shè)置 cost
<?php /** * 在這個(gè)案例里,我們?yōu)?BCRYPT 增加 cost 到 12。 * 注意,我們已經(jīng)切換到了,將始終產(chǎn)生 60 個(gè)字符。 */ $options = [ 'cost' => 12, ]; echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n"; ?> // 輸出類似于: // $2y$12$QjSH496pcT5CEbzjD/vtVeH03tfHKFy36d4J0Ltp3lRtee9HDxY3K
示例3,如何選擇一個(gè)適合當(dāng)前服務(wù)器的 cost
<?php /** * 這個(gè)例子對(duì)服務(wù)器做了基準(zhǔn)測(cè)試(benchmark),檢測(cè)服務(wù)器能承受多高的 cost * 在不明顯拖慢服務(wù)器的情況下可以設(shè)置最高的值 * 8-10 是個(gè)不錯(cuò)的底線,在服務(wù)器夠快的情況下,越高越好。 * 以下代碼目標(biāo)為 ≤ 50 毫秒(milliseconds), * 適合系統(tǒng)處理交互登錄。 */ $timeTarget = 0.05; // 50 毫秒(milliseconds) $cost = 8; do { $cost++; $start = microtime(true); password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]); $end = microtime(true); } while (($end - $start) < $timeTarget); echo "Appropriate Cost Found: " . $cost . "\n"; ?>
輸出類似于:
Appropriate Cost Found: 10
password_needs_rehash
說(shuō)明
boolean password_needs_rehash ( string $hash , integer $algo [, array $options ] )
參數(shù)
- hash, 一個(gè)由 password_hash() 創(chuàng)建的散列值。
- algo, 一個(gè)用來(lái)在散列密碼時(shí)指示算法的密碼算法常量。
- options, 一個(gè)包含有選項(xiàng)的關(guān)聯(lián)數(shù)組。目前支持兩個(gè)選項(xiàng):
salt,在散列密碼時(shí)加的鹽(干擾字符串),
cost,用來(lái)指明算法遞歸的層數(shù)。這兩個(gè)值的例子可在 crypt() 頁(yè)面找到。
示例,
<?php $password = 'rasmuslerdorf'; $hash = '$2y$10$YCFsG6elYca568hBi2pZ0.3LDL5wjgxct1N8w/oLR/jfHsiQwCqTS'; // cost 參數(shù)可隨硬件的提升也不斷提升 $options = array('cost' => 11); // 使用純文本密碼 驗(yàn)證存儲(chǔ)的散列 if (password_verify($password, $hash)) { // 檢查是否有更新的散列算法可用或 cost 是否已經(jīng)改變 if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) { // 如果是,請(qǐng)創(chuàng)建一個(gè)新的哈希值,并替換舊的哈希值 $newHash = password_hash($password, PASSWORD_DEFAULT, $options); } // 用戶登錄驗(yàn)證完成 // ... } ?>
password_verify
說(shuō)明
boolean password_verify ( string $password , string $hash )
注意 password_hash() 返回的哈希包含了算法、 cost 和鹽值。 因此,所有需要的信息都包含內(nèi)。使得驗(yàn)證函數(shù)不需要儲(chǔ)存額外鹽值等信息即可驗(yàn)證哈希。
參數(shù)
- password, 用戶的密碼。
- hash, 一個(gè)由 password_hash() 創(chuàng)建的散列值。
示例,
<?php // 想知道以下字符從哪里來(lái),可參見(jiàn) password_hash() 的例子 $hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq'; if (password_verify('rasmuslerdorf', $hash)) { echo 'Password is valid!'; } else { echo 'Invalid password.'; } ?>
以上例程會(huì)輸出:
Password is valid!
PHP5.50 之前的密碼哈希 API
安東尼·費(fèi)拉拉(PHP原生密碼哈希 API的開(kāi)發(fā)者)為PHP5.5.0 以下的版本也提供了 ircmaxell/password-compat組件(https://packagist.org/packages/ircmaxell/password-compat)。
這個(gè)組件也實(shí)現(xiàn)了PHP密碼哈希API中的所有函數(shù),
- password_get_info
- password_hash
- password_needs_rehash
- password_verify
我們可以直接使用 Composer 把這個(gè)組件添加到我們的應(yīng)用中就行了。例如,
composer require ircmaxell/password-compat
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- 詳解PHP處理密碼的幾種方式
- php登陸頁(yè)的密碼處理方式分享
- 一個(gè)簡(jiǎn)單的網(wǎng)頁(yè)密碼登陸php代碼
- php+mysql實(shí)現(xiàn)簡(jiǎn)單登錄注冊(cè)修改密碼網(wǎng)頁(yè)
- php+MySQL實(shí)現(xiàn)登錄時(shí)驗(yàn)證登錄名和密碼是否正確
- php password密碼驗(yàn)證正則表達(dá)式(8位長(zhǎng)度限制)
- 允許phpmyadmin空密碼登錄的配置方法
- php中實(shí)現(xiàn)記住密碼下次自動(dòng)登錄的例子
- 理解php Hash函數(shù),增強(qiáng)密碼安全
- php中實(shí)現(xiàn)記住密碼自動(dòng)登錄的代碼
相關(guān)文章
詳解PHP實(shí)現(xiàn)執(zhí)行定時(shí)任務(wù)
這篇文章主要介紹了PHP實(shí)現(xiàn)執(zhí)行定時(shí)任務(wù)的具體實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-12-12PHP數(shù)字金額轉(zhuǎn)換成中文大寫(xiě)顯示
今天小編就為大家分享一篇關(guān)于PHP數(shù)字金額轉(zhuǎn)換成中文大寫(xiě)顯示,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01UCenter中的一個(gè)可逆加密函數(shù)authcode函數(shù)代碼
瀏覽UCenter源代碼的時(shí)候發(fā)現(xiàn)這個(gè)函數(shù),剛好有需要,就記錄一下。2010-07-07php創(chuàng)建基本身份認(rèn)證站點(diǎn)的方法詳解
本篇文章是對(duì)php創(chuàng)建基本身份認(rèn)證站點(diǎn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06PHP利用ThinkPHP6完整實(shí)現(xiàn)用戶分頁(yè)功能
分頁(yè)功能在Web開(kāi)發(fā)中是一個(gè)非常常見(jiàn)的需求,特別是在處理大量數(shù)據(jù)時(shí),為了提高用戶體驗(yàn)和性能,將數(shù)據(jù)分頁(yè)展示是必不可少的,ThinkPHP6作為一款流行的PHP框架,自帶了強(qiáng)大的分頁(yè)功能,本文將詳細(xì)介紹如何使用ThinkPHP6實(shí)現(xiàn)分頁(yè)功能,需要的朋友可以參考下2023-12-12PHP中Array相關(guān)函數(shù)簡(jiǎn)介
在php教程中數(shù)組是種強(qiáng)大的數(shù)據(jù)類型,他可以做的事情很多,可以存儲(chǔ)不同的數(shù)據(jù)類型在一個(gè)數(shù)組中,下面我們列出了數(shù)組常用的操作,排序,鍵名對(duì)數(shù)組排序等做法。2016-07-07實(shí)現(xiàn)了一個(gè)PHP5的getter/setter基類的代碼
實(shí)現(xiàn)了一個(gè)PHP5的getter/setter基類的代碼...2007-02-02