简介

在数据库中存储密码是非常常见的需求,但是直接在数据库中保存明文密码明显是不合适的。万一数据库泄漏,攻击者能拿到所有的帐号密码信息,所以,存在数据库中的密码必须加密,且这种加密必须是单向的。即正向运算很简单,但反向运算基本不可能。例如告诉你两个很大的素数,要你计算他们的乘积很简单,但是反过来,告诉你一个大数,要你求解出他是由哪两个大素数乘起来就很难。

关于本文内容,在 php 官方文档 上也有对应描述。本文讲解的是我个人的理解。

加密算法

直接使用单向散列算法(md5,sha1),仍会存在隐患。这种算法最大的缺陷就是同样的内容加密后输出结果相同。有很多用户都是使用弱密码的,比如123456之类这种,那破解者只要算出123456的密文是多少,然后数据库里所有存储都是这个密文的人的密码就都是123456。为此很多人会在密码基础上拼一个随机字符串再计算md5,这个方式虽然提高了一定的安全性,但增加了各流程的复杂度,且php本身已经提供了这种解决方案。

用password_hash保存密码

php 官方提供了一个password_hash函数,具体文档可以查看 文档

函数原型

1
password_hash ( string $password , int $algo [, array $options ] ) : string

第一个参数是明文密码,第二参数指定加密算法,第三个可选参数是配置项,不同的算法允许不同的配置项。

支持的算法有:

  • PASSWORD_DEFAULT: 使用 bcrypt 算法 (PHP 5.5.0 默认)。 注意,该常量会随着 PHP 加入更新更高强度的算法而改变。 所以,使用此常量生成结果的长度将在未来有变化。 因此,数据库里储存结果的列可超过60个字符(最好是255个字符)。
  • PASSWORD_BCRYPT - 使用 CRYPT_BLOWFISH 算法创建散列。 这会产生兼容使用 “$2y$” 的 crypt()。 结果将会是 60 个字符的字符串, 或者在失败时返回 FALSE。
  • PASSWORD_ARGON2I - 使用 Argon2 散列算法创建散列。

我一般使用第二个算法,我的常用调用是 password_hash('123456', PASSWORD_BCRYPT),该调用返回的就是加密后的密码,直接存储在数据库里即可。如果你想查看更详细的说明,请点击上面的文档。

散列加密后的秘文格式分解图如下所示:

用password_verify验证密码

由于密码是密文存储的,且难以反向求出原文密码,所以你能做的就是再那用户传过来的密码用相同的加密方式计算一下,如果得到的密文和数据库里存储的一致,那说明密码正确。

php 提供了password_verify函数,用于实现上述过程,函数原型如下:

1
password_verify ( string $password , string $hash ) : bool

第一个参数是待验证的明文密码,第二个参数是密文。若返回结果参数为true,说明密码验证通过。

结论

借助这两个函数,就可以安全的实现密码的存储和校验,不用再费心的折腾别的了。