172

这比普通的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]!";
}
4

7 回答 7

273

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

因为安全性往往比大多数程序员单独解决的问题要复杂得多,并且存在更多不可见的搞砸可能性,所以使用标准库几乎总是最简单和最安全的(如果不是唯一的)可用选项。


新的 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 年)建议在(自动)加盐密码哈希中添加“胡椒”。

有一个简单的类可以安全地实现这种模式,我推荐: 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';
}


旧标准库

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

看看:便携式 PHP 密码散列框架phpass并确保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 的模块)
  • 其他

好处是您不必担心细节,这些细节已经由有经验的人编写,并由互联网上的许多人审查。

有关密码存储方案的更多信息,请阅读Jeff的博客文章:您可能错误地存储了密码

无论你做什么,如果你采取“我会自己做,谢谢你”的方法,不要使用MD5orSHA1。它们是很好的散列算法,但出于安全目的被认为是损坏的

目前,使用crypt和 CRYPT_BLOWFISH 是最佳实践。
PHP 中的 CRYPT_BLOWFISH 是 Bcrypt 哈希的实现。Bcrypt 基于 Blowfish 分组密码,利用其昂贵的密钥设置来减慢算法速度。

于 2009-10-17T10:36:22.740 回答
30

如果您使用参数化查询而不是连接 SQL 语句,您的用户将更加安全。并且对于每个用户应该是唯一的,并且应该与密码哈希一起存储。

于 2009-10-17T06:57:10.763 回答
12

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

使用盐的好处是攻击者更难预先生成每个字典单词的 MD5 签名。但是如果攻击者知道你有一个固定的盐,他们就可以预先生成每个字典单词的 MD5 签名,以你的固定盐为前缀。

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

于 2009-10-17T07:37:27.813 回答
12

随着 PHP 5.5(我所描述的甚至更早的版本也可用,见下文)即将到来,我想建议使用其新的内置解决方案:password_hash()password_verify(). 它提供了几个选项来实现您需要的密码安全级别(例如,通过$options数组指定“成本”参数)

<?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."

如您所见,该字符串包含盐以及选项中指定的成本。它还包含使用的算法。

因此,在检查密码时(例如当用户登录时),当使用免费password_verify()功能时,它会从密码哈希本身中提取必要的加密参数。

当不指定盐时,每次调用时生成的密码哈希都会不同,password_hash()因为盐是随机生成的。因此,即使密码正确,将先前的哈希值与新生成的哈希值进行比较也会失败。

验证工作如下:

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 文件)可以password_hash在 PHP 5.3.7+ 中为您提供 PHP 5.5:https ://github.com/ircmaxell/password_compat

于 2013-06-06T09:20:51.367 回答
0

这对我来说没问题。Atwood 先生写了关于MD5 对彩虹表的强度,基本上用长盐,你坐得很漂亮(虽然一些随机的标点符号/数字,它可以改善它)。

您还可以查看 SHA-1,它最近似乎越来越流行。

于 2009-10-17T06:58:21.703 回答
0

我想补充:

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

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

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

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

  • 更新用户密码的哈希值

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

于 2016-11-04T16:05:58.607 回答
0

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

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

CMS登录代码:https ://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/boot.php#L56 调用 https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7//function.php/ L171

应用程序特定的业务代码: https ://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/ajax_login.php#L87 调用 https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee660413209766667/a 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

于 2021-09-02T03:58:14.173 回答