【问题标题】:Correct way to store and retrieve SHA-256 hashed and salted passwords存储和检索 SHA-256 散列和加盐密码的正确方法
【发布时间】:2014-03-04 13:50:55
【问题描述】:

这是我第一次尝试安全地存储密码,我想确保一切都正确完成。有人建议我将 SHA-256 散列与 salt 一起使用。

假设用户提交了完整的密码表单,我们通过

获取密码
$password = $_POST["password"];

什么是加盐 $password 并在其上使用 SHA-256 散列的正确方法,因此它可以存储在数据库中的密码字段“密码 CHAR(64)”中?

完成并存储后,我将如何将数据库中存储的值与登录表单中输入的用户进行比较?让我们假设 $loginPassword = $_POST["loginPassword"]; 是用户输入的内容。

【问题讨论】:

  • 建议使用 SHA-256?那不是what I've heard
  • @ÁlvaroG.Vicario 很有趣,作为今天使用的另一种散列方法,您有什么建议?最好是安全和快速?我认为它可以通过与 SHA 相同的逻辑来应用?
  • 它不可能安全快。在链接的手册页中使用 Blowfish 的提示很好恕我直言。 (还有一些其他类似的算法,但它们是专门设计为慢的,就像 Blowflish 一样。)
  • 使用crypt()函数;如果您有兴趣,它会为您处理盐分。

标签: php security hash passwords salt


【解决方案1】:

您可以使用crypt() 函数为您添加盐,而不是使用 SHA 系列方法。

这是一个使用 PDO 的示例脚本(保存和登录)。

在数据库中保存密码

<?php
// Set the password
$password = 'mypassword';

// Get the hash, letting the salt be automatically generated
$hash = crypt($password);

echo $hash; // for testing purposes only

$mysql_username = 'username'; // for DB
$mysql_password = 'password'; // for DB

$dbh = new PDO('mysql:host=localhost;dbname=database_name', $mysql_username, $mysql_password);

$stmt = $dbh->prepare("INSERT INTO table_name (name,pass) VALUES (:name,:pass)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':pass', $pass);

// insert rows
// $name = $_POST['name'];
// $name = $_POST['pass'];

$name = "username";
$pass = $hash;
$stmt->execute();

登录脚本

<?php
$mysql_username = 'username'; // for DB
$mysql_password = 'password'; // for DB

$dbh = new PDO('mysql:host=localhost;dbname=database_name', $mysql_username, $mysql_password);

/*
$username = $_POST['username'];
$password = $_POST['password'];
*/

$username = "username";
$password = "mypassword";

$sql = "SELECT * FROM table_name WHERE name=:username";
$statement = $dbh->prepare($sql);
$statement->bindValue(':username',$username,PDO::PARAM_STR);

if($statement->execute())
{
    if($statement->rowCount() == 1)
    {
        $row = $statement->fetch(PDO::FETCH_ASSOC);

 if (crypt($password, $row['pass']) === $row['pass'])

        {
            $username = $row['name'];
            $email = $row['email'];

echo "Stage 1";

echo "<hr noshade size=\"1\">";

echo "Hello " .$username;

            exit;
        }
        else
        {
            // include "error_login.php";

echo "Stage 2 - ERROR";

        }
    }
    else
    {
       // include "error_login.php";

echo "Stage 3 error";
    }
}

【讨论】:

  • 只有几个问题,在登录脚本中,当您 bindBalue :username PDO::PARAM_STR 用于什么?同样 PDO::FETCH_ASSOC ?这是我第一次看到这些。
  • This page 将更详细地解释它。 @Ilja 和 PDO manual
  • 以这种形式使用的加密货币将生成一个没有安全盐的弱 DES 哈希。您应该明确地使用函数password_hash()。看看 Anti-weakpasswords 的答案。
  • 我同意,但正如我在回答中所说,这是一个示例脚本(可以修改)。如果 OP 在服务器上有 PHP 5.5,那么一定要使用 PHP 的 password() 函数。 @martinstoeckli
  • @Fred-ii- - 请注意,对于 PHP 5.3.7 及更高版本,还有一个简单的 PHP 文件形式的 compatibility pack。对于 5.3.7 之前的版本,我写了 example code 来使用 BCrypt。
【解决方案2】:

如果您使用的是 PHP 5.5 或更高版本,则有内置的 password_hash() 和 password_verify() 以及 Bcrypt - 如果您使用的是 PHP 5.3.7 或更高版本,则有 password_compat 兼容性库;这一切都是根据PHP.net Safe Password Hashing FAQ entry

基本上,在 PHP 5.3.7 及更高版本上,将旧的 crypt() 替换为 password_hash() 和 password_verify()。

有关成本选择的更多详细信息,请参阅my answer to PHP Secure password generation and storage,但归结为非常简单:

<?php
/**
 * In this case, we want to increase the default cost for BCRYPT to 12.
 * Note that we also switched to BCRYPT, which will always be 60 characters.
 */
$options = [
    'cost' => 12,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>

生成哈希,然后存储输出字符串,然后验证:

<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
?>

这两个例子都来自PHP.net Password Hashing page

【讨论】:

  • 太棒了,我唯一不明白的是 $hash 位,我是否只需将其值设置为密码哈希生成的随机密码?
  • 根据文档,“使用的算法、成本和盐作为散​​列的一部分返回。因此,验证散列所需的所有信息都包含在其中。” - 因此在验证中,$hash 设置为等于初始 password_hash() 的值;只有当用户设置或更改密码时,您才会使用 password_hash() - password_verify() 是您在登录时使用的,首先传入他们输入的密码,然后传入您存储在您的数据库/文本文件/您正在使用的任何存储。
猜你喜欢
  • 2015-02-17
  • 2015-08-20
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
  • 2011-10-29
  • 2014-06-10
  • 2011-12-29
相关资源
最近更新 更多