vanqard/passman

用于密码散列、验证密码散列以及测试是否需要重新散列密码的超级简单的密码散列管理器。完全符合 oo 包装器。

2.1.1 2020-06-17 14:45 UTC

This package is not auto-updated.

Last update: 2024-09-19 10:43:09 UTC


README

Build Status

此库提供围绕 PHP5.5+ 内置的或通过 Anthony Ferrara 的 password_compatibility 库提供的 password_hash() 函数的对象包装器。

意图

提供简单易用、轻量级、与框架无关的包,同时符合它所消耗的底层 password_hash() 函数,并暴露给这些函数的对象接口。

此包的存在是为了简化关于用户密码散列的最佳实践。当前 password_hash() 函数缺乏对象上下文,导致开发者不得不编写自己的逻辑,有时甚至是错误的逻辑来使用这些函数。

因此,此包的目标是提供一个现成的、面向对象实现,可以由社区共同维护最佳实践。这包括禁用向底层 password_hash() 函数提供自定义盐的能力,从而确保所有盐都是动态生成的。

设计目标

  • 超级轻量级但仍完全符合规范
  • 为访问 password_* 函数提供面向对象上下文
  • 与框架无关,但易于添加到任何项目中
  • 可扩展性,随着新 PASSWORD_* 算法的出现而扩展
  • 优先禁用用户提供的 $salt,以自动动态加盐
  • 通过简单易用的接口鼓励更好的密码散列实践

致谢

这项工作受到 PHP 的过程式 password_* 函数和 Anthony Ferrara 为旧版本语言提供的密码散列兼容库的启发。在某些情况下,我甚至直接引用了他的 README.md 中的某些单词,因为我认为用我的风格重写它们是不必要的,而且在很多情况下,我无法更简洁或更清楚地表达这个想法。

要求

为了使用 password_hash() 函数,此库需要 PHP >= 5.3.7。这是自身就符合此包所依赖的、用于 PHP 版本大于 5.3.7 但小于 5.5 的 ircmaxell\password_compat 库的最小要求,该库包含原生函数。

安装

请通过 composer 安装此包。请参阅 vanqard/passman

这可以通过简单地执行 composer require 命令来实现

php composer.phar require vanqard/passman

或者通过将以下行添加到 composer.json 文件的 require 部分

"vanqard/passman": "*"

一旦进入 composer.json,运行 composer update 应确保您拥有最新且成功构建的标签

检查您的环境

首先,在您开始在项目中使用此库之前,您应该确定系统首选的 'cost' 值。您的目标是使散列过程尽可能昂贵,但又不至于严重影响用户的使用体验。PHP 手册 提供了一个脚本来允许您对系统进行基准测试,我在这里重现了它,但将其目标时间提高到了手册规定的 50ms 以上。

<?php
// You may need to amend this path to locate composer's autoloader
require('vendor/autoload.php'); 

$timeTarget = 0.25; // 250 milliseconds 

$cost = 8;
do {
    $cost++;
    $start = microtime(true);
    password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]);
    $end = microtime(true);
} while (($end - $start) < $timeTarget);

echo "Appropriate Cost Found: " . $cost . "\n";
?>

理想情况下,您正在寻找一个将散列时间设置为 100 到 500 毫秒的 cost 值。cost 值越高,生成的散列越强。

使用方法

现在您已安装了软件包,接下来您需要获取一个预置了合适哈希算法的 PasswordManager。PasswordManager 类提供了一个简单的工厂方法来简化这个过程。

工厂方法期望一个算法标识符(基于 PASSWORD_* 常量)以及可选的该算法的选项数组。预期您的应用程序将通过您通常使用的任何机制提供这些配置选项。

// Simple method
use Vanqard\PassMan\PasswordManager;

$defaultType = PASSWORD_DEFAULT;
$defaultOptions = array("cost" => 10);

$passwordManager = PasswordManager::factory($defaultType, $defaultOptions);

// Alternative method - delayed instantiation via a DIC
$passManClosure = function($defaultType, $defaultOptions) {
    return PasswordManager::factory($defaultType, $defaultOptions);
}

// Slim example
$app->passman = $passManClosure;

// Pimple example
$pimple['passman'] = $passManClosure;

一旦您拥有了 PasswordManager 实例,您就可以访问相关的方法。方法名称是符合 PSR-1 的驼峰命名法的本地函数名称。

创建密码哈希

当您接收到需要哈希的密码时,您将调用 passwordHash() 方法,如下所示

$userPassword = $_POST['password'];

$hashedPassword = $passwordManager->passwordHash($userPassword);

您将像通常一样将 $hashedPassword 值存储在您的数据库中。

验证密码

当用户想要登录时,您的代码需要确认他们提供的密码是否正确。在这种情况下,您将使用 passwordVerify() 方法来获取布尔值 true 以表示匹配成功,或者当提供的密码与您存储的哈希版本不匹配时返回 false。

$userPassword = $_POST['password'];
$storedHash = $dbResult['password_hash'];

if ($passwordManager->passwordVerify($userPassword, $storedHash)) {
    // successful match - you may proceed to log the user in
     
        ...

} else {
    // Passwords mismatch
    throw new \RuntimeException('credentials do not match');
}

重新哈希密码

有时,您可能决定需要更新您的哈希参数(算法、成本等),例如通过将应用程序迁移到更好的硬件。

在这种情况下,您需要修改代码以反映新的哈希参数,但请记住,您的数据库中存储的值是使用旧参数创建的。

此函数允许您在用户登录时逐步更新用户的密码的算法/成本值。这将帮助您避免强制系统范围内的密码重置,否则这将对用户的体验造成很大的干扰。

该方法的简单形式如下

$boolean = $passwordManager->passwordNeedsRehash($storedHash);

在常规使用中,您会在登录过程中这样使用它

// During the login process
$userPassword = $_POST['password'];
$storedHash = $dbResult['password_hash'];

if ($passwordManager->passwordVerify($userPassword, $storedHash)) {
    if ($passwordManager->passwordNeedsRehash($storedHash)) {
    	    $newHash = $passwordManager->passwordHash($userPassword);
    	    // store the new hash value in the user's record
    }
    // Proceed with your login process
    
} else {
    // Passwords mismatch
    throw new \RuntimeException('credentials do not match');
}

安全漏洞

如果您在此代码中发现了任何安全漏洞,请直接联系作者:thunder@vanqard.com