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!'; }