当前位置: 首页 > 后端技术 > PHP

PHP密码哈希算法学习

时间:2023-03-30 05:46:10 PHP

不知道大家有没有看过Laravel的源码。在Laravel源码中,password_hash()函数用于对用户密码进行加密。该函数属于PHP密码哈希算法扩展中包含的函数。是PHP源码中集成的一个扩展,也是PHP官方推荐的一种密码加密方式。那么它有什么好处呢?password_hash()系列函数其实就是对crypt()加密函数的封装。crypt()函数也是一种单向散列函数。默认情况下,它基于UNIXDES算法。此函数的盐值是一个可选参数。如果没有盐值,它会生成一个简单的弱密码。所以在PHP5.6之后,如果crypt()函数没有给salt值,就会报warning错误。而password_hash()在其基础上增加了一套可靠的随机盐值生成器封装在这个函数中。具体内容,我们将通过下面的代码一步步学习。查看密码哈希函数的加密算法首先,我们看一下当前环境支持的password_hash()算法。print_r(password_algos());//Array//(//[0]=>2y//)可以看出,在当前环境下,我们只有算法2y可以使用,而这个函数也只是由PHP7.4提供。让我们简单地了解一下。使用密码哈希函数对数据进行加密,重点介绍该加密函数的应用。我们来看看password_hash()函数的使用。PHP5.5以后已经提供了这个功能,大家可以放心使用。echopassword_hash("thisispassword",PASSWORD_DEFAULT),PHP_EOL;//$2y$10$vOI56sADJPhebhzq5Bj1quM7grMex3Y4NlI99C3qP83iveEGnfdd.echopassword_hash("thisispassword",PASSWORD_DEFAULT),PHP_EOL;//$2y$10$YMq8zsTw32HCOeWmlLSpruWKiSoO/rlNu2OVcIV4hlVSY4enn8GwS没错,就是这么简单的说,PASSWORD_DEFAULT就是我们指定的加密算法,这里我们给一个默认值。但是,加密后的数据并不是像md5()那样的十六进制字符串。是的,password_hash()加密的内容不是md5类型的Hash字符串,而是一组类似于JWT的加密字符串。关于JWT的内容大家可以自行了解。这里,最重要的是password_hash()加密的内容和JWT是一样的。加密后的字符串中包含了一些信息,比如加密循环次数、盐值信息等。此信息对于我们稍后执行密码匹配是必需的。又有人说了,既然有salt值,为什么我们不定义这个salt值,那后面怎么匹配呢?如前所述,加密字符串本身已经包含盐值信息,而盐值信息是系统随机生成的。只有使用相应的比较函数才能比较原始明文密码和加密后的密码。一致,这样可以大大提高系统的安全性。请注意上面的测试代码,我们两个代码的明文是一样的,但是加密后的密码哈希是完全不同的。当然,更重要的是,加密后的密码也是不可逆的,是一种常规的单向哈希。所以它是一个非常安全的密码加密功能,这也是官方推荐的原因。那么,我们可以指定它的盐值吗?当然。$options=['cost'=>12,];echopassword_hash("rasmuslerdorf",PASSWORD_BCRYPT,$options),PHP_EOL;//$2y$12$YjEdiCJHAmPCoidNvgrZq.k4VH3ShoELWlyU9POHD5sV3L1WW4.vS$options=['1cost,'=>'salt'=>mcrypt_create_iv(22,MCRYPT_DEV_URANDOM),];echopassword_hash("rasmuslerdorf",PASSWORD_BCRYPT,$options);//$2y$11$syLcOhq1Mfc32cWVi1zyLOvSn.AtcCre.kY999uUXZ6pS3nXNv1是最后一个参数PHP中的选项数组,其中,cost代表加密循环次数(循环加密多少次),salt当然是我们的salt值,这里是mcrypt_create_iv()生成的,我们也可以用自己生成的随机字符串作为salt。不过关键是PHP7之后,option参数数组中的salt已经被标记为废弃了。如果使用它,将报告弃用警告。也就是说,官方是希望我们不要使用自定义的salt来加密,而是使用系统自动随机生成的默认salt。因此,在我们日常使用中,可以直接使用第一行代码的形式进行加密。如果有特殊需要,可以指定成本来改变循环次数。不同周期的数量取决于当前系统的硬件。当然更高的数值需要系统更高的硬件支持。默认情况下,此值为10。查看加密字符串信息$p=password_hash('thisispassword',PASSWORD_DEFAULT,$options);print_r(password_get_info($p));//Array//(//[algo]=>2y//[algoName]=>bcrypt//[options]=>Array//(//[cost]=>11//)//)是一个很简单的函数,可以帮助我们看到这个加密数据的加密信息,简单来说说关于返回信息的内容。algo是使用的加密算法。我们看到当前系统只有2y算法,所以只能使用PASSWORD_DEFAULT作为默认算法。algoName是算法的人类可读名称,我们算法的正式名称是bcrypt算法。options数组其实就是我们给的option参数的内容。从这个函数可以看出,加密字符串中确实包含了算法的信息。验证密码哈希数据格式是否一致有时,我们想升级当前密码强度,比如增加密码循环次数,数据库中新旧算法的密码混合记录在一起。这个时候怎么办?var_dump(password_needs_rehash($p,PASSWORD_DEFAULT,$options));//bool(false)var_dump(password_needs_rehash($p,PASSWORD_DEFAULT,['cost'=>5]));//bool(true)password_needs_rehash()是PHP提供的内容,用于比较当前加密的字符串是否与我们提供的算法和选项一致。如果一致,则返回false。如果不是,它将返回true。嗯,这又有点令人困惑了,它不应该始终返回true吗?其实从函数名就可以看出,这个函数的意思是密码(password)是否需要(needs)重新哈希(rehash)。也就是说,如果算法和选项一致,那么密码不需要重新哈希,当然返回false,如果算法或选项不一致,则需要重新哈希密码,并且返回值是正确的。大家千万不要反过来用。验证密码最后,也是最重要的,当我们要验证明文密码和加密后的密码是否一致时,我们应该怎么做呢?如果是原来的md5方式,我们可以用同样的方式加密明文密码,然后用双等号进行比较。但是password_hash()就不行了,因为它的salt是随机的,我们不需要保存,所以即使是同一个字符串,也不能保证每次加密的结果都是一样的,所以我们必须使用系统为我们提供的验证功能。var_dump(password_verify('这是密码',$p));//bool(true)var_dump(password_verify('1thisispassword',$p));//bool(false)也是一个很简单的函数,第一个参数是明文密码,第二个是加密后的密码,函数内部会比较它们的信息。此外,该比较函数还具有防御定时攻击的能力,其对任意循环次数的密码进行比较的返回时间为固定长度。定时攻击的内容请自行百度。总结既然这套函数已经成为PHP推荐的函数,那自然是我们以后应该学习的重点内容。甚至PHP框架中的大部分用户型密码加密也是用的这套函数。我们不需要再使用md5加密方式了,数据库还要保存我们自己的salt值浪费数据库空间。直接使用password_hash()既方便又安全。测试代码:https://github.com/zhangyue0503/dev-blog/blob/master/php/202007/source/PHP%E5%AF%86%E7%A0%81%E6%95%A3%E5%88%97%E7%AE%97%E6%B3%95%E7%9A%84%E5%AD%A6%E4%B9%A0.php参考文档:https://www.php.net/manual/zh/book.password.phphttps://www.php.net/manual/zh/book.password.php各媒体平台均可搜索【硬核项目经理】