一、认识 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 Functions | Hash value 的长度 (bit) |
CRC32 | 32 |
MD5 | 128 |
SHA-1 | 160 |
(在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