crypt是一個單向的字符串哈希方法。php
string crypt ( string $str [, string $salt ] )
$str
是須要進行哈希的字符串。算法
$salt
是進行哈希時使用鹽值,是個可選項。安全
例如如下代碼:.net
<?php echo crypt('mypassword');
會輸出相似的結果:code
$1$1Y.rRSxY$htFPrMkCbV.Av.yh2lTJd.
注意:從PHP 5.6.0開始,若是沒有傳$salt參數,會報E_NOTICE錯誤。文檔
咱們既沒有指定使用的算法,也沒有指定鹽值,crypt
是怎麼知道使用什麼算法和鹽值的呢?其實crypt是根據$salt參數來判斷使用哪一種哈希算法。也就是說,$salt自己就包含了算法的類型以及哈希時實際用到鹽值。字符串
咱們先來看看,若是沒有傳$salt的話,crypt會如何處理。 在5.3版本前,PHP在安裝的時候會根據系統的crypt()
方法檢測可用的算法。若是沒有提供$salt參數,PHP會使用自動產生一個標準的兩個字符(DES)的鹽值或者一個12個字符(MD5)的鹽值,視乎MD5算法是否可用。get
從PHP 5.3版本開始,PHP包含了自身實現的crypt算法,包括MD五、標準DES、擴展DES和Blowfish算法。若是系統不支持這些算法,就會使用PHP本身實現的。例如上面那個例子,最終使用的是MD5算法的鹽值。input
PHP設置了一個常量
CRYPT_SALT_LENGTH
,用來表示鹽值最大容許的長度。string
那crypt是怎麼根據鹽值判斷不一樣的算法呢?其實它是按照必定規則去匹配鹽值,若是符合某個算法的規則,就表示使用哪一種算法。咱們逐一看下目前支持的幾種算法。
在沒有匹配到其餘算法的狀況下,則使用標準DES算法,此時取前兩個字符爲鹽值(不足兩個字符則返回*0
):
<?php echo crypt('rasmuslerdorf', 'rl'); // 輸出結果以下: // rl.3StKT.4T8M
鹽值的字符必須是./0-9A-Za-z
裏的字符,不然會引發 crypt()失敗(我的測了下,好像字符的範圍要比文檔裏說的大)。
使用這個算法時,
$str
只取前面8個字符,因此不管字符串有多長,若是前8個字符同樣,在鹽值同樣的狀況下,返回的哈希值也是同樣的。
如下劃線_
開頭,後面緊接着4字節的迭代次數和4字節的鹽值:
<?php echo crypt('rasmuslerdorf', '_J9..rasm'); // 輸出結果以下: // _J9..rasmBYk8r9AiWNc
一樣的,那8個字節的字符必須是./0-9A-Za-z
裏的字符。
以$1$
開頭,而後是12個字符之內的鹽值:
<?php echo crypt('rasmuslerdorf', '$1$rasmusle$'); // 輸出結果以下: // $1$rasmusle$rISCgZzpwk3UhDidwXvin0
其實例子裏的$1$rasmusle$
最後一位改爲任何字符都不會影響結果,例如$1$rasmuslea
和$1$rasmusle1
獲得的結果跟$1$rasmusle$
是同樣的,由於最後一位必定是$
,沒有的話會自動補上。例如$1$rasm
會變成$1$rasm$
,$1$
會變成$1$$
。
以$2a$
、$2x$
或者$2y$
開頭,而後是用於cost
參數的兩位數字,緊接着一個$
字符,最後是22位./0-9A-Za-z
範圍裏的字符:
<?php echo crypt('rasmuslerdorf', '$2a$07$usesomesillystringforsalt$'); // 輸出結果以下: // $2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi
兩位數字用於表示Blowfish算法的迭代次數,是以2位底的對數,範圍爲04-31
。在PHP 5.3.7版本以前,只支持$2a$
做爲前綴,5.3.7以後,增長了$2x$
和$2y$
兩種前綴,用於解決一些安全性問題。若是使用5.3.7版本或以上,建議使用$2y$
。
使用這個算法的時候,
$str
最長支持72個字符,超過會被截掉。
以$5$
開頭,而後是16個字符的鹽值,鹽值以前還可使用rounds=<N>$
的格式代表哈希的循環次數(N):
<?php echo crypt('rasmuslerdorf', '$5$rounds=5000$usesomesillystringforsalt$'); // 輸出結果以下: // $5$rounds=5000$usesomesillystri$KqJWpanXZHKq2BOB43TSaYhEWsQ1Lr5QNyPCDH/Tp.6
rounds
的默認值爲5000,範圍是1000到999,999,999,若是N不在這個範圍裏,會被截取到最接近的範圍裏。
PHP 5.3.2增長的算法,算法的實現基於Ulrich Drepper的實現。
以$6$
開頭,而後是16個字符的鹽值,鹽值以前還可使用rounds=<N>$
的格式代表哈希的循環次數(N):
<?php echo crypt('rasmuslerdorf', '$6$rounds=5000$usesomesillystringforsalt$'); // 輸出結果以下: // $6$rounds=5000$usesomesillystri$D4IrlXatmP7rx3P3InaxBeoomnAihCKRVQP22JZ6EY47Wc6BkroIuUUBOov1i.S5KPgErtP/EN5mcO.ChWQW21
rounds
的默認值爲5000,範圍是1000到999,999,999,若是N不在這個範圍裏,會被截取到最接近的範圍裏。
PHP 5.3.2增長的算法,算法的實現基於Ulrich Drepper的實現。
crypt
提供瞭如下的常量來標識是否支持某個算法(1表示支持,0表示不支持):
CRYPT_STD_DES
CRYPT_EXT_DES
CRYPT_MD5
CRYPT_BLOWFISH
CRYPT_SHA256
CRYPT_SHA512
從上面的那些例子的輸出能夠看到,crypt
方法輸出的結果其實包含了鹽值自己。因此咱們能從輸出結果的自己知道這個結果是使用哪一個算法和什麼鹽值進行運算的。
另外,若是鹽值包含了非法的字符,例如一般鹽值都要求是./0-9A-Za-z
範圍裏的字符,若是不是的話,crypt
會返回結果*0
。另外,在5.5.21以前的5.5分支版本以及5.6.5以前的5.6分支版本,若是$salt
的值爲*0
,會返回一個使用DES算法的哈希值,而以後的版本則返回*1
。
要驗證一字符串的哈希值,咱們通常是用相同的鹽值相同的算法進行一次運算,而後跟以前的值進行比較。而使用crypt
方法,因爲它的輸出結果自己就帶有算法和鹽值的信息,咱們只須要把輸出結果當作$salt
參數便可:
<?php $hashed_password = crypt('mypassword'); if ($hashed_password === crypt($user_input, $hashed_password)) { echo 'Password verified!'; }
爲了防止基於時間的攻擊,PHP 5.6提供了一個更爲安全的字符串比較方法hash_equals()
,建議使用:
<?php $hashed_password = crypt('mypassword'); if (hash_equals($hashed_password, crypt($user_input, $hashed_password))) { echo 'Password verified!'; }