今天主要研究PHP中Hash散列加密相关的一些扩展函数的使用,而不是Hash算法。这种加密其实只是一种比较复杂的密钥算法,类似于Hash算法,我们输入的一串字符串,就像一个Hash表一样,都有它对应的Hash哈希值,本质上和普通数据中的Hash键值映射是一样的结构,只是算法有点复杂。其实,只要你做过一段时间的PHP开发,就一定对两个函数不陌生,它们是md5()和sha1()。这两个函数分别是生成md5和sha1算法的Hash加密。但是,我们今天学习的比这两个函数要复杂,算法形式也更加丰富。什么是哈希信息摘要算法通常,我们将一段内容输入哈希函数后,返回的哈希字符串字符串就是输入值的哈希信息摘要。在PHP中,无论是md5还是sha1,相同的输入产生相同的结果。因此,如果我们保存用户密码信息,尽量不要只使用一层Hash,因为这种加密方式可以被rainbowtable暴力破解。我们可以对密码进行多层散列,并加盐使散列值复杂化。当然Hash算法并不局限于我们常用的md5和sha1,还有很多其他类型的算法,只是我们不经常用到。不过,今天介绍的函数只是一组可以进行各类Hash加密的函数。它们已经集成到PHP中的默认环境中,我们无需单独扩展即可使用它们。这样一来,为我们的加密数据多样化带来了更多的方便。PHP支持的Hash算法print_r(hash_algos());//Array//(//[0]=>md2//[1]=>md4//[2]=>md5//[3]=>sha1//[4]=>sha224//[5]=>sha256//[6]=>sha384//[7]=>sha512/224//[8]=>sha512/256//[9]=>sha512//[10]=>sha3-224//[11]=>sha3-256//[12]=>sha3-384//[13]=>sha3-512//[14]=>ripemd128//[15]=>ripemd160//[16]=>ripemd256//[17]=>ripemd320//[18]=>漩涡//[19]=>tiger128,3//[20]=>tiger160,3//[21]=>tiger192,3//[22]=>tiger128,4//[23]=>tiger160,4//[24]=>tiger192,4//[25]=>snefru//[26]=>snefru256//[27]=>gost//[28]=>gost-crypto//[29]=>adler32//[30]=>crc32//[31]=>crc32b//[32]=>fnv132//[33]=>fnv1a32//[34]=>fnv164//[35]=>fnv1a64//[36]=>joaat//[37]=>haval128,3//[38]=>haval160,3//[39]=>haval192,3//[40]=>haval224,3//[41]=>haval256,3//[42]=>haval128,4//[43]=>haval160,4//[44]=>haval192,4//[45]=>haval224,4//[46]=>haval256,4//[47]=>haval128,5//[48]=>haval160,5//[49]=>haval192,5//[50]=>haval224,5//[51]=>haval256,5//)$data="让我们测试哈希算法!";foreach(hash_algos()as$v){$r=hash($v,$data);echo$v,':',strlen($r),'::',$r,PHP_EOL;}//md2:32::3d63d5f6ce9f03379fb3ae5e1436bf08//md4:32::e9dc8afa241bae1bccb7c58d4de8b14d//md5:32::2801b208ec396a2fc80225466e17acac//sha1:40::0f029efe9f1115e401b781de77bf1d469ecee6a9//sha224:56::3faf937348ec54936be13b63feee846d741f8391be0a62b4d5bbb2c8//sha256:64::8f0bbe9288f6dfd2c6d526a08b1fed61352c894ce0337c4e432d97570ae521e3//sha384:96::3d7d51e05076b20f07dad295b161854d769808b54b784909901784f2e76db212612ebe6fe56c6d014b20bd97e5434658//...foreach(hash_hmac_algos()as$v){$r=hash_hmac($v,$data,'secret');echo$v,':',strlen(::'r)$r,PHP_EOL;}//md2:32::70933e963edd0dcd4666ab9253a55a12//md4:32::d2eda43ee4fab5afc067fd63ae6390f1//md5:32::68bf5963e1426a1feff8149da0d0b88d//sha1:40::504bc44704b48ac75435cdccf81e0f056bac98ba//sha224:56::8beaf35baedc2cd5725c760ec77d119e3373f14953c74818f1243f69//sha256:64::23f2e6685fe368dd3ebe36e1d3d672ce8306500366ba0e8a19467c94e13ddace//sha384:96::740ce7488856737ed57d7b0d1224d053905661ffca083c02c6a9a9230499a4a3d96ff0a951b8d03dbafeeeb5c84a65a6//……通过hash_algos()和hash_hmac_algos()函数,我们就可以获取到当前PHP环境中所支持的所有Hash算法,我们可以见到熟悉的md5和sha1,youcanalsoseemd2,sha224,ripemd320,fnv1a64andotherrarealgorithms.Thenwetraversethecontentreturnedbythesetwofunctions,andusethehash()andhash_hmac()functionstohashthedataandLookingattheircontentsshowsthateachalgorithmsuccessfullyreturnsadifferentdigestoftheencryptedmessage,andwithadifferentnumberofbits.Thehmac-relatedfunctionisanotherformofPHP'sHashalgorithm,whichisanalgorithmthatrequiresakey,whichisthethirdparameterofhash_hmac().只有相同的输入内容和相同的键才会返回相同的结果。也就是说,该函数可用于对称加密信息传输验证令牌。比如两个系统之间的互通需要一个固定的token,就可以使用这个函数来实现。对比md5(),sha1()这个hash()函数这么强大,那么它生成的内容和md5一样吗?//与md5sha1函数比较echohash('md5','来测试Hash算法吧!'),PHP_EOL;echomd5('让我们测试哈希算法!'),PHP_EOL;//2801b208ec396a2fc80225466e17acac//2801bash208ec396a2fc8022ac466eha1('sha1','让我们测试哈希算法!'),PHP_EOL;echosha1('Let'stesttheHashalgorithm!'),PHP_EOL;//0f029efe9f1115e401b781de77bf1d469ecee6a9//0f029efe9f1115e401b781de77bf1d469nechoecashee6a(Let'stest4'CheckouttheHashalgorithm!'),PHP_EOL;//b25bd7371f08cea4Ofcoursethereisnodoubtaboutthis,even我感觉md5()和sha1()这两个函数是hash()函数的语法糖。因为这两个算法太常用了,PHP直接给我们封装了当前的两个函数,而且只需要一个参数,非常简单方便。FileHASH很多下载站点都会提供下载文件的Hash值,供我们查看和比较,以确定下载的文件是否完全相同。这就是文件哈希的应用。其实说白了,就是把文件内容提取出来,进行Hash哈希后得到的文件信息的汇总。当然,这套功能在我们的PHP中也是完美支持的。/文件HASHechohash_file('md5','./create-phar.php'),PHP_EOL;echomd5_file('./create-phar.php'),PHP_EOL;//ba7833e3f6375c1101fb4f1d130cf3d3//ba7833e3f6375c1101fb4f1d130cf3d3',hashmacfile_d3''./create-phar.php','secret'),PHP_EOL;//05d1f8eb7683e190340c04fc43eba9dbhkdf和pbkdf2的HASH算法下面两种算法是特殊的两种Hash算法。类似于hmac,但比hmac复杂一点。//hkdfpbkdf2算法//算法明文密码(原始二进制)输出长度应用程序/上下文特定信息字符串盐值$hkdf1=hash_hkdf('sha256','123456',32,'aes-256-encryption',random_bytes(2));$hkdf2=hash_hkdf('sha256','123456',32,'sha-256-authentication',random_bytes(2));var_dump($hkdf1);var_dump($hkdf2);//字符串(32)"°`q??X??l??//f??ye????}Ozb+??"//string(32)"%????]+???\JdG????HL??GK??//-"//算法明文密码盐值迭代次数数据长度echohash_pbkdf2("sha256",'123456',random_bytes(2),1000,20),PHP_EOL;//e27156f9a6e2c55f3b72hmac只需要一个key就好了,hash_hkdf()增加三个参数:返回长度,application/context-specificinformationstring,saltvalue,加密后的内容是二进制加密内容,是不是很爽!而hash_pbkdf2()增加了盐值、迭代次数和数据长度三个参数,也是密码加密的好帮手。但相对来说,它们的使用要复杂一些。如果是对安全性要求非常高的密码,可以使用这两个功能。hash_equals()函数进行Hash比较PHP也为我们提供了比较Hash值是否相等的函数。有朋友要问了,既然返回的是字符串形式的汇总信息,直接===不行,为什么还要专门的函数来比较呢?别着急,我们先来看代码。//hash_equals比较函数$v1=hash('md5','testcomparison');$v2=hash('md5','测试比较');$v3=hash('md5','testcomparison1');//比较两个字符串,无论是否相等,该函数耗时不变//该函数可用于字符串比较场景需要防止定时攻击的,比如可以用来比较crypt()密码哈希值的场景var_dump(hash_equals($v1,$v2));var_dump(hash_equals($v1,$v3));//bool(true)//bool(false)我在注释里写的很清楚了,hash_equals()函数主要是为了防止时序攻击。一般来说,这种定时攻击就是根据你的系统运行的时间长短来判断你的系统使用了哪些函数或函数。这是非常强大的黑客玩的东西。比如我们比较用户密码的时候,假设是一个一个比较,那么如果第一个字符错误,很快就会返回信息,如果直到最后一个字符都比较错误,程序运行时间会减少。时间会很长,黑客可以根据这段时间的长短,一步步判断当前暴力破解的内容是否达到目标,破解难度会逐渐降低。(普通字符串比较===是基于位移的)。而hash_equals()的意思是无论怎么比较,相同Hash算法长度的内容返回时间都是一样的。OpenSSL、OpenSSH等软件都存在过这种定时攻击漏洞!当然,我们只需要了解这一点,还有一些项目对安全性有特殊的要求,那么我们就可以利用这个功能来规避此类时序攻击漏洞,提高系统安全性。增量Hash运算最后需要学习的是一套增量Hash运算函数。其实对于字符串来说,大多数情况下,我们可以直接将字符串进行拼接,然后进行哈希,并不需要增量哈希的能力。但是,如果想要获取多个文件或者读写流的Hash值,可以使用这套递增的Hash函数来操作。//递增HASH$fp=tmpfile();fwrite($fp,'初始化流文件');倒带($fp);$h1=hash_init('md5');//开始递增Hashhash_update($h1,'testincrement');//纯字符串hash_update_file($h1,'./create-phar.php');//文件hash_update_stream($h1,$fp);//流$v1=hash_final($h1);//结束哈希并返回结果echo$v1,PHP_EOL;//373df6cc50a1d7cd53608208e91be1e7$h2=hash_init('md5',HASH_HMAC,'secret');//使用HMAC算法自增HASHhash_update($h2,'testincrement');hash_update_file($h2,'./create-phar.php');hash_update_stream($h2,$fp);$v2=hash_final($h2);echo$v2,PHP_EOL;//34857ee5d8b573f6ee9ee20723470ea4我们使用hash_init()获取增量Hash操作句柄并指定加密算法。然后使用hash_update()添加字符串,使用hash_update_file()添加文件内容,使用hash_update_stream()添加流内容,最后使用hash_final()结束句柄操作进行Hash计算并返回结果值。结果值是将字符串、文件和流内容一起散列的结果。总结说实话,在学习今天的内容之前,我一直以为PHP中只有两种Hash算法,md5和sha1。这次真是大开眼界。我们不仅有丰富的算法库,还有很多方便的操作函数,帮助我们方便的使用这些算法。话不多说,继续学习吧!测试代码:https://github.com/zhangyue0503/dev-blog/blob/master/php/202007/source/PHP%E7%9A%84Hash%E4%BF%A1%E6%81%AF%E6%91%98%E8%A6%81%E6%89%A9%E5%B1%95%E6%A1%86%E6%9E%B6.php参考文档:https://www.php.net/manual/zh/book.hash.phphttps://www.zhihu.com/question/20156213https://baike.baidu.com/item/%E6%97%B6%E5%BA%8F%E6%94%BB%E5%87%BB/17882818?fr=aladdin所有媒体平台均可搜索【硬核项目经理】
