欢迎您的光临,本博所发布之文章皆为作者亲测通过,如有错误,欢迎通过各种方式指正。

教程  php进阶(六)--PHP的hash函数及hash算法性能比较

PHP学习 本站 1567 0评论

一、认识 PHP hash函数


hash函数:https://www.php.net/manual/zh/ref.hash.php


Hashing function (散列函式) 在网页应用中被广泛采用,从数码签署、错误检测、登入验证、到压缩储存空间,由于它的原理比较复杂,很多人把它跟加密函式混淆,对于如何运用hash function,如何选择合适的hash function,和它的优点缺点都不清楚,本文尝试解答这些问题。


简单地说,Hashing 是一种数据影射(mapping) 的算法(algorithm),通常用来把一大串不定长度的数据影射到一个固定长度的、较短的数据,这个固定长度的数据称为hashing value (散列值)。


例如我们把一个由英文字母组成的任意长度的字串,把每一个字符的ASCII 数值加起来,最后除以256 得到的余数作为hash value,这里输入的字串长度没有限制,输出的数值则必定在0 至255 之间,所以是一个合法的hashing function。


以上的hash function 只有256 个可能的hash value,很明显有很多字串都会得到相同的hash value,这种情况我们称为hash collision (散列冲突),或者简称collision,事实上从一个不定长度的数据影射到一个固定长度的数据,Collision 是无可避免的,我们并不要求完全没有collision,只需把collision 的机会尽量降低便可以了,若果真的要完全没有collision 的话,Hash value 理论上必须与输入的数据长度相同,这样便违背了hash function 的设计目的。


现实应用的hashing function 通常比较复杂,比较有名的包括MD4、MD5、SHA1、SHA256 等,它们的hash value 的数量从2 的几十次方到几百次方。其实我们任何人都可以自行设计一个hashing function,不过基于hashing function 的实际用途,我们对hashing function 有一些基本要求,在进一步解释前,让我们看看hashing 有什么常见的用途。


二、Hashing 的用途


1.数码签署


很多提供程式下载的网站,都会在网页上列出下载档案的hash value,比较常见的是MD5 码,下载的人可以自行计算下载回来的档案的hash value 是否与网站提供的相符,从而验证这个程式是否曾经被修改,这个过程就是数码签署。数码签署的概念可以应用在很多通讯领域,例如你要发送一个很重要的电子邮件给别人,为了让收件者放心内容在传送过程中没有被其他人擅改,你可以另外告诉收件人电子邮件的MD5 码,让他自行验证。


在这种用途中,理想的hashing function 应该具备两种特性,首先是任何对原本文件的改动都会令产生的hash value 改变;第二是没有方法可以得知如何该动原本的文件使计算出来的hash value 相同。


当然,我们还要确保hash value 不会在传送途中被人拦截并且修改,但这属于通讯安全的问题,超越了hash function 的讨论。


2.错误检测


资料在网络上传送的时候,会受到很多干扰而使内容改变,其中包括网络问题、电脑硬件问题、电脑程式问题等,为了检验资料的正确性,我们可以一并把资料的hash value 发送给收件者,让收件者比对自行计算的hash value 和收到的hash value 来确认资料的正确性。


在这种用途种中,理想的hash function 跟上面的要求差不多,就是任何对原本资料的改动都会令产生的hash value 改变。


3.登入验证


在伺服器上储存用户的系统密码是有风险的,第一这样做等于把密码的安全交托给伺服器管理员,他们一定可靠吗?别忘记密码万一泄漏背黑镬的是你而不是他们啊;第二很多用户把相同的密码应用在很多不同的系统(这样做当然很不好,但你无法限制用户不可以这样做),当一个系统被黑客入侵泄漏了用户的密码,他们在其他系统的帐号也同时中门大开,后果可以很严重。为了保障用户,设计良好的系统都不会直接储存用户的密码,只会储存密码的hash value。用户登入时输入的密码,会被转换成hash value,然后与伺服器上储存的hash value 比较来进行身分验证。


这种用途的hash function,必须是不可能返过来从hash value 计算原本的密码。此外,由于collision 的缘故,只要找到一个密码,它的hash value 与用户的密码的hash value 相同,便可以冒认这名用户登入系统,无须知道真正的密码,所以hash value 的数量必须非常庞大,使collision 的可能性很低很低,使寻找这个「伪冒」密码的人要付出很大的代价。


4.压缩储存空间


Hash function 其中一个最经典的用途是制作hashing table (散列表),它可说是一个关联阵列(associative array),阵列的指标是一些不定长度的数据或者是比较复杂的数据结构,很多高阶编程语言包括PHP、Perl、gawk 等都支援关连阵列,背后的原理就是利用hash function 把这些数据转换成数字,然后读取阵列中的元素。在大部分的情况下,作为阵列指标的数据可以非常庞大,但是阵列的长度(元素的数量) 相对来说却很少,所以冲突的情况会比较突出,从用户(编程人员) 的角度冲突是不应该发生的,不同的数据便应该对应到不同的阵列位置,所以这些语言都有某些方法来处理冲突。


用hash table 来实作关联阵列的好处是搜索资料的速度高,无论有多少资料,搜索的速度都是固定的,这一点对于要处理大量数据的应用很重要。


三、PHP 有什么 hashing 工具?


在PHP5 之前我们只有CRC32、MD5 和SHA1 三个内置的hash function,它们输出的hash value 长度如下:

Hash FunctionsHash value 的长度 (bit)
CRC3232
MD5128
SHA-1160

(在PHP5.12以后可以使用 hash_algos()返回所有的hash算法,并从手册上得知现支持 35种算法;查看手册


其中SHA-1 可说是最多人使用的hash function,原因是它的hash value 比其他的大,Collision 的机会便小得多。其次SHA 家族的hashing functions 是由美国国家安全部(NSA – National Security Agency) 设计,并被列为美国联邦资讯处理标准的一部分,所以给人较高的信心,很多复杂的安全方案例如SSL 都使用SHA-1。


PHP 还有两个需要额外安装的函式库支援更多hash function,就是mhash 和hash,Hash 从PHP 5.1.2 开始列为标准的模组,无须自行编译或安装,所以越来越多人使用。一些比SHA-1 更先进的hash function 都可以在这两个函式库中找到,例如属于SHA-2 家族的SHA-256 和SHA-512 等,不过由于SHA-1 的历史比较悠久,很多系统仍然继续使用它,尤其是用SHA-1 来进行登入验证的系统,由于hash function 的不可还原性,很难一下子改用其他hash function。


使用SHA-1 的方法很简单(PHP 的函式大都很简单,不是吗?):

echo sha1("I am a happy boy");


Hash 的用法也很简单:

echo hash("sha256","I am a happy boy.");


Hash 支援很多hash function,可以用hash_algo 查看你的PHP 版本支援什么:

print_r(hash_algos());


四、password_hash 创建哈希密码


PHP 5.5 时引入一个给密码加密的方法,叫 password_hash。它的使用方法如下:

$passwordHash = password_hash('123456', PASSWORD_BCRYPT);

// to do with $passwordHash ...


不可逆

上面的操作是将明文密码 123456 使用 CRYPT_BLOWFISH 算法处理成一个由 60 个字符组成的字符串,类似 $2y$10$3qI4IKS6XOiisBDTTgp17eruMdcd3dDJaqaB6pQkEHR0Uk7od2A1a,这称之为「哈希值」。

经过 password_hash 方法加密得到的哈希值有个特点:不可逆——不能从这个哈希值反推出明文密码(也就是之前的 123456)。你可能傲娇了,心想我就知道你密码设定不复杂,直接用 password_hash('123456', PASSWORD_BCRYPT) 得到哈希值,再细心的和 $2y$10$3qI4IKS6XOiisBDTTgp17eruMdcd3dDJaqaB6pQkEHR0Uk7od2A1a 比对,发现一样,不就 OK 了……这也行不通,因为每次执行password_hash('123456', PASSWORD_BCRYPT) 语句后,得到哈希值都不一样!


盐值

这是因为 password_hash 再给密码做哈希之前,会先加入一个随机子串,因为加入的随机子串每次是不一样的,所以得到的哈希值自然就不一样了。这就让在不同的服务中使用同一个密码的用户,他的密码的安全性变高了。

这个随机子串就叫「盐值」,加入盐值的过程就是「加盐处理」。


password_verify

用 password_hash 加密的密码,可以用 password_verify 方法验证。

<?php
// $hash 值从 `password_hash()` 方法得到 
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';
 
if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
?>

在做用户更新密码功能时,验证旧密码就是用 password_verify 方法。


五、PHP中各种Hash算法性能比较


今天做的模块又用到了Hash函数,突然想起Hash函数可能会比较占CPU资源,所以希望使用一种速度最快的摘要函数。但是PHP中的Hash函数很多,MD4、MD5、SHA-1、SHA-256、SHA-384以及SHA-512,都是比较常见的安全领域的HASH应用。于是写了个程序对比了一下PHP支持的各种Hash函数:

<?php
define('testtime', 50000);
$algos = hash_algos();
foreach($algos as $algo) {
    $st = microtime();
    for($i = 0; $i < testtime; $i++) {
        hash($algo, microtime().$i);
    }
    $et = microtime();
    list($ss, $si) = explode(' ', $st);
    list($es, $ei) = explode(' ', $et);
    $time[$algo] = $ei + $es - $si - $ss;
}
asort($time, SORT_NUMERIC);
print_r($time);
?>

此程序测试每种hash函数支持的算法,对50000个字符串执行hash计算,然后将耗时按从低到高排序,结果如下:

Array
(
    [crc32b] => 1.14942403926
    [crc32] => 1.15080493481
    [adler32] => 1.17250810205
    [md4] => 1.21484698894
    [md5] => 1.25582505324
    [sha256] => 1.31992111638
    [ripemd256] => 1.34005199425
    [ripemd128] => 1.34174097336
    [sha1] => 1.34424093234
    [ripemd160] => 1.36161398381
    [haval128,3] => 1.37490507759
    [haval160,3] => 1.37925811601
    [haval192,3] => 1.37971906387
    [haval224,3] => 1.38690299403
    [haval256,3] => 1.38968507692
    [tiger128,3] => 1.40321999939
    [tiger192,3] => 1.42025405684
    [tiger160,3] => 1.42113689062
    [ripemd320] => 1.42461802158
    [haval128,4] => 1.4465580045
    [haval160,4] => 1.44935391309
    [haval192,4] => 1.45606506625
    [haval224,4] => 1.4650528846
    [tiger128,4] => 1.47951410777
    [tiger192,4] => 1.49081709387
    [haval256,4] => 1.50713596634
    [haval160,5] => 1.51613600436
    [haval224,5] => 1.51645894888
    [haval192,5] => 1.51678603177
    [haval256,5] => 1.51900808377
    [tiger160,4] => 1.52507308815
    [haval128,5] => 1.53689793875
    [whirlpool] => 1.82801189377
    [snefru] => 1.85931909387
    [gost] => 1.89863007236
    [sha384] => 1.95804009064
    [sha512] => 1.97130295938
    [md2] => 4.99702701607
)

CRC是冗余验证算法,不适合用来做唯一标识符Hash计算,MD4是最快的摘要算法,MD5次之,SHA系列算法居然是SHA-256最快,比SHA-1还快一些。由此得出结论:要把唯一标识符转换成定长字串可以考虑使用MD4,而密码加密则SHA-1或SHA-256更合适。MD5就没有多少使用的必要了,速度比不过MD4,安全性比不过SHA,还是趁早放弃的好。


各种Hash结果如下:

<?php
$data = "hello world";
foreach (hash_algos() as $v) {
        $r = hash($v, $data, false);
        printf("%-12s %3d %s\n", $v, strlen($r), $r);
}
?>

md2           32 d9cce882ee690a5c1ce70beff3a78c77

md4           32 aa010fbc1d14c795d86ef98c95479d17

md5           32 5eb63bbbe01eeed093cb22bb8f5acdc3

sha1          40 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed

sha224        56 2f05477fc24bb4faefd86517156dafdecec45b8ad3cf2522a563582b

sha256        64 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

sha384        96 fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcbb83578b3e417cb71ce646efd0819dd8c088de1bd

sha512       128 309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f

ripemd128     32 c52ac4d06245286b33953957be6c6f81

ripemd160     40 98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f

ripemd256     64 0d375cf9d9ee95a3bb15f757c81e93bb0ad963edf69dc4d12264031814608e37

ripemd320     80 0e12fe7d075f8e319e07c106917eddb0135e9a10aefb50a8a07ccb0582ff1fa27b95ed5af57fd5c6

whirlpool    128 8d8309ca6af848095bcabaf9a53b1b6ce7f594c1434fd6e5177e7e5c20e76cd30936d8606e7f36acbef8978fea008e6400a975d51abe6ba4923178c7cf90c802

tiger128,3    32 4c8fbddae0b6f25832af45e7c62811bb

tiger160,3    40 4c8fbddae0b6f25832af45e7c62811bb64ec3e43

tiger192,3    48 4c8fbddae0b6f25832af45e7c62811bb64ec3e43691e9cc3

tiger128,4    32 24465a3f6e4aa92d903ee535476591e9

tiger160,4    40 24465a3f6e4aa92d903ee535476591e937f3a14d

tiger192,4    48 24465a3f6e4aa92d903ee535476591e937f3a14d81c4c7b6

snefru        64 902b49fa8b0828b44d8ac069111899bbfaf51d334485e4b28e90c93f63bb86dd

snefru256     64 902b49fa8b0828b44d8ac069111899bbfaf51d334485e4b28e90c93f63bb86dd

gost          64 1bb6ce69d2e895a78489c87a0712a2f40258d1fae3a4666c23f8f487bef0e22a

gost-crypto   64 c5aa1455afe9f0c440eec3c96ccccb5c8495097572cc0f625278bd0da5ea5e07

adler32        8 1a0b045d

crc32          8 7813f744

crc32b         8 0d4a1185

fnv132         8 548da96f

fnv1a32        8 d58b3fa7

fnv164        16 7dcf62cdb1910e6f

fnv1a64       16 779a65e7023cd2e7

joaat          8 3e4a5a57

haval128,3    32 906c1df7cbe6d318f36ab172f95e89c0

haval160,3    40 6e733b21876e47c2168a122e23d86bdd69e50f95

haval192,3    48 ec67a6a417953fdbf3496502004b6c21b270d5890dedd931

haval224,3    56 766879d9ba1dc9e24a6040908a7ae813a47b08af5c5f3beebcacda48

haval256,3    64 45492c6c8adab277759f4381420799431a037daf6d829b8b5c21104c10f61a92

haval128,4    32 c97d46956b8e3e60acd2bb090c482c5e

haval160,4    40 2cb8b12eb5a2561022010c2a2af8795e602fdef2

haval192,4    48 39a281e4e492533b6dfea0af294149ccac771ab87204c9ec

haval224,4    56 3d64d34aea48f5e649ed6147da5d29d31c762a937e9e21f4da1f3106

haval256,4    64 0359a526d77e271707c44d9b270e68a394f8486a459f0137ad5e1d02e44c5889

haval128,5    32 8332ad9f32e385d9acd421b63ee04cfc

haval160,5    40 d33cf9052d55da9b0f506cb8849097939363e361

haval192,5    48 67c3492878c8fc4819c8589231fcfe69b15b015c1ca48ac5

haval224,5    56 6bedeb6a8676e46413c020c8813c022486ca93353b8a0673fb577ba1

haval256,5    64 f5f6ffcfe39a65ac2c3989430340420341762a6624ebd69b9d08ec1dc4b9f167


参考网址:

https://www.php.net/manual/zh/book.hash.php

https://blog.csdn.net/u012217533/article/details/46525457 

https://blog.csdn.net/baidu_28393027/article/details/80540132 

https://www.cnblogs.com/shangzekai/p/4444169.html 


转载请注明: ITTXX.CN--分享互联网 » php进阶(六)--PHP的hash函数及hash算法性能比较

最后更新:2019-04-10 13:24:27

赞 (3) or 分享 ()
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽