PHP笔记 —— crypt方法

关于方法

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参数来判断使用哪一种哈希算法。也就是说,&dollar;salt自己就包含了算法的类型以及哈希时实际用到盐值字符串

咱们先来看看,若是没有传&dollar;salt的话,crypt会如何处理。 在5.3版本前,PHP在安装的时候会根据系统的crypt()方法检测可用的算法。若是没有提供&dollar;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(Standard DES)

在没有匹配到其余算法的状况下,则使用标准DES算法,此时取前两个字符为盐值(不足两个字符则返回*0):

<?php

echo crypt('rasmuslerdorf', 'rl');

// 输出结果以下:
// rl.3StKT.4T8M

盐值的字符必须是./0-9A-Za-z里的字符,不然会引发 crypt()失败(我的测了下,好像字符的范围要比文档里说的大)。

使用这个算法时,$str只取前面8个字符,因此不管字符串有多长,若是前8个字符同样,在盐值同样的状况下,返回的哈希值也是同样的。

扩展DES(Extended DES)

如下划线_开头,后面紧接着4字节的迭代次数和4字节的盐值:

<?php

echo crypt('rasmuslerdorf', '_J9..rasm');

// 输出结果以下:
// _J9..rasmBYk8r9AiWNc

一样的,那8个字节的字符必须是./0-9A-Za-z里的字符。

MD5

$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$$

Blowfish

$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个字符,超过会被截掉。

SHA256

$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的实现

SHA512

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

参考

相关文章
相关标签/搜索