如何安全地储存用户的密码?

这比普通的 MD5安全多少?我刚开始查密码安全。我对 PHP 还是个新手。

$salt = 'csdnfgksdgojnmfnb';


$password = md5($salt.$_POST['password']);
$result = mysql_query("SELECT id FROM users
WHERE username = '".mysql_real_escape_string($_POST['username'])."'
AND password = '$password'");


if (mysql_num_rows($result) < 1) {
/* Access denied */
echo "The username or password you entered is incorrect.";
}
else {
$_SESSION['id'] = mysql_result($result, 0, 'id');
#header("Location: ./");
echo "Hello $_SESSION[id]!";
}
72511 次浏览

如果使用参数化查询而不是连接 SQL 语句,用户将会安全得多。对于每个用户都应该是唯一的,并且应该与密码散列一起存储。

我没意见。阿特伍德先生写了关于 MD5对抗彩虹桌的强度的文章,基本上,像这样的长盐,你坐得很漂亮(虽然一些随机的标点符号/数字,它可以改善它)。

你也可以看看 SHA-1,它现在似乎越来越流行了。

一个更好的方法是让每个用户都有一个独特的盐。

使用 salt 的好处是,它使攻击者更难预先生成每个字典单词的 MD5签名。但是,如果攻击者知道您有一个固定的 salt,那么他们可以预先生成每个字典单词的 MD5签名,这些字典单词的前缀都是您的固定 salt。

更好的方法是每次用户更改密码时,系统生成一个随机的 salt 并将该 salt 与用户记录一起存储。它使得检查密码的开销有点大(因为在生成 MD5签名之前需要查找 salt) ,但是它使得攻击者更难预先生成 MD5签名。

使您的密码存储方案安全的最简单方法是使用 使用标准库

由于安全性往往比大多数程序员单独处理的要复杂得多,而且存在更多看不见的搞砸的可能性,因此使用标准库几乎总是最简单和最安全(如果不是唯一的话)的可用选项。


新的 PHP 密码 API (5.5.0 +)

如果您使用的是 PHP 版本5.5.0或更新的版本,则可以使用新的简化密码散列 API

使用 PHP 密码 API 的代码示例:

<?php
// $hash is what you would store in your database
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT, ['cost' => 12]);


// $hash would be the $hash (above) stored in your database for this user
$checked = password_verify($_POST['password'], $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}

(如果您仍在使用旧版本5.3.7或更新版本,可以安装 Ircmaxell/password _ compat来访问内置函数)


改善腌制的饼干: 加入胡椒粉

如果需要额外的安全性,安全专家现在(2017)建议在(自动)加盐的密码哈希中添加一个“ Pepper”。

有一种简单的、安全地实现这种模式的方法,我推荐: Netsilik/PepperedPasswords (Github).
它附带有 MIT 许可证,因此您可以随意使用它,甚至在专有项目中也是如此。

使用 Netsilik/PepperedPasswords的代码示例:

<?php
use Netsilik/Lib/PepperedPasswords;


// Some long, random, binary string, encoded as hexadecimal; stored in your configuration (NOT in your Database, as that would defeat the entire purpose of the pepper).
$config['pepper'] = hex2bin('012345679ABCDEF012345679ABCDEF012345679ABCDEF012345679ABCDEF');


$hasher = new PepperedPasswords($config['pepper']);


// $hash is what you would store in your database
$hash = $hasher->hash($_POST['password']);


// $hash would be the $hash (above) stored in your database for this user
$checked = $hasher->verify($_POST['password'], $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}


旧的标准库

请注意: 你不应该再需要这个了! 这只是用于历史目的。

看一下: < strong > 便携式 PHP 密码哈希框架 : 等等,如果可能的话,请确保使用 CRYPT_BLOWFISH算法。

使用 phpass (v0.2)的代码示例:

<?php
require('PasswordHash.php');


$pwdHasher = new PasswordHash(8, FALSE);


// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );


// $hash would be the $hash (above) stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}

在一些相当知名的项目中实施了 PHPass:

  • PhpBB3
  • WordPress 2.5 + 以及 bbPress
  • Drupal 7发行版(Drupal 5 & 6可用的模块)
  • 其他人

好消息是,你不需要担心细节,这些细节已经由有经验的人编程,并由许多人在互联网上审查。

有关密码存储方案的更多信息,请阅读 杰夫的博客文章: 您可能存储的密码不正确

无论你做什么,如果你去’我自己来,谢谢’的方法,不再使用 ABC0或 SHA1。他们是很好的哈希算法,但认为 为了安全起见而被破坏

目前,使用 地窖和 CRYPT _ BLOWFISH 是最佳实践。
PHP 中的 CRYPT _ BLOWFISH 是 Bcrypt 散列的实现。Bcrypt 基于 Blowfish 分组密码,利用其昂贵的密钥设置来降低算法的速度。

随着 PHP 5.5(我所描述的对于更早的版本也是可用的,见下文)的临近,我想建议使用它的新的内置解决方案: password_hash()password_verify()。它提供了几个选项,以实现所需的密码安全级别(例如,通过 $options数组指定“ Cost”参数)

<?php
var_dump(password_hash("my-secret-password", PASSWORD_DEFAULT));


$options = array(
'cost' => 7, // this is the number of rounds for bcrypt
// 'salt' => 'TphfsM82o1uEKlfP9vf1f', // you could specify a salt but it is not recommended
);
var_dump(password_hash("my-secret-password", PASSWORD_BCRYPT, $options));
?>

会回来的

string(60) "$2y$10$w2LxXdIcqJpD6idFTNn.eeZbKesdu5y41ksL22iI8C4/6EweI7OK."
string(60) "$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d."

正如您可能看到的,字符串包含 salt 以及在选项中指定的成本。它还包含所使用的算法。

因此,当检查密码时(例如当用户登录时) ,当使用免费的 password_verify()函数时,它将从密码散列本身提取必要的加密参数。

如果没有指定 salt,则在每次调用 password_hash()时生成的密码散列将是不同的,因为 salt 是随机生成的。因此,将以前的散列与新生成的散列进行比较将会失败,即使是对于正确的密码也是如此。

验证工作如下:

var_dump(password_verify("my-secret-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("wrong-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));


var_dump(password_verify("my-secret-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
var_dump(password_verify("wrong-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));

我希望提供这些内置函数将很快提供更好的密码安全性,以防数据被盗,因为它减少了程序员在正确实现中必须花费的精力。

有一个小库(一个 PHP 文件) ,它将为您提供 PHP 5.5在 PHP 5.3.7 + : https://github.com/ircmaxell/password_compat中的 password_hash

我想补充一点:

  • 不要限制用户密码的长度

为了与旧系统兼容,常常设置密码的最大长度限制。这是一个糟糕的安全策略: 如果您设置了限制,那么仅将其设置为密码的最小长度。

  • 不要通过电子邮件发送用户密码

为了恢复忘记的密码,您应该发送用户可以更改密码的地址。

  • 更新用户密码的哈希表

密码哈希可能过期(算法的参数可能会更新)。通过使用函数 password_needs_rehash(),您可以检查它。

这是一个 PHP + couchdb.apache.org 登录系统,它不存储明文密码。

根据我读到的建议,它应该是完全安全的。

CMS 登录代码: < a href = “ https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/boot.php # L56”rel = “ nofollow noReferrer”> https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/boot.php#l56 电话 Https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/functions.php#l171

应用程序的特定业务代码: Https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/ajax_login.php#l87 电话 Https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/functions.php#l230 反过来又叫做: Https://github.com/nicerapp/nicerapp/blob/2d479b3e22dce9e7073525481b775f1bf7389634/nicerapp/apps/nicer.app/webmail/recrypt.php#l2

并将 webmail 应用程序配置数据编辑到数据库中: Https://github.com/nicerapp/nicerapp/blob/main/nicerapp/apps/nicer.app/webmail/ajax_editconfig.php