简体   繁体   English

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

[英]How can I store my users' passwords safely?

How much more safe is this than plain MD5 ?这比普通的MD5安全多少? I've just started looking into password security.我刚刚开始研究密码安全。 I'm pretty new to PHP.我对 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]!";
}

The easiest way to get your password storage scheme secure is by using a standard library .确保密码存储方案安全的最简单方法是使用标准库

Because security tends to be a lot more complicated and with more invisible screw up possibilities than most programmers could tackle alone, using a standard library is almost always easiest and most secure (if not the only) available option.因为安全性往往比大多数程序员单独解决的要复杂得多,而且有更多不可见的搞砸可能性,所以使用标准库几乎总是最简单和最安全(如果不是唯一的话)的可用选项。


The new PHP password API (5.5.0+)新的 PHP 密码 API (5.5.0+)

If you are using PHP version 5.5.0 or newer, you can use the new simplified password hashing API如果您使用的是 PHP 5.5.0 或更高版本,则可以使用新的简化密码散列 API

Example of code using PHP's password 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';
}

(In case you are still using legacy 5.3.7 or newer you can install ircmaxell/password_compat to have access to the build-in functions) (如果您仍在使用旧版 5.3.7 或更新版本,您可以安装ircmaxell/password_compat以访问内置函数)


Improving upon salted hashes: add pepper改进盐渍哈希:加入胡椒粉

If you want extra security, the security folks now (2017) recommend adding a ' pepper ' to the (automatically) salted password hashes.如果您想要额外的安全性,安全人员现在 (2017) 建议在(自动)加盐密码哈希中添加“ 胡椒”。

There is a simple, drop in class that securely implements this pattern, I recommend: Netsilik/PepperedPasswords ( github ).有一个简单的类可以安全地实现这种模式,我推荐: Netsilik/PepperedPasswords ( github )。
It comes with a MIT License, so you can use it however you want, even in proprietary projects.它带有 MIT 许可证,因此您可以随心所欲地使用它,即使在专有项目中也是如此。

Example of code using Netsilik/PepperedPasswords :使用Netsilik/PepperedPasswords的代码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';
}


The OLD standard library旧标准库

Please note: you should not be needing this anymore!请注意:您不应该再需要这个了! This is only here for historical purposes.这只是为了历史目的。

Take a look at: Portable PHP password hashing framework : phpass and make sure you use the CRYPT_BLOWFISH algorithm if at all possible.看一看: Portable PHP password hashing framework : phpass并确保尽可能使用CRYPT_BLOWFISH算法。

Example of code using phpass (v0.2):使用 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 has been implemented in some quite well known projects: PHPass 已经在一些非常著名的项目中实现:

  • phpBB3 phpBB3
  • WordPress 2.5+ as well as bbPress WordPress 2.5+ 以及 bbPress
  • the Drupal 7 release, (module available for Drupal 5 & 6) Drupal 7 版本,(可用于 Drupal 5 和 6 的模块)
  • others其他

The good thing is that you do not need to worry about the details, those details have been programmed by people with experience and reviewed by many folks on the internet.好处是你不需要担心细节,这些细节是由有经验的人编写的,并被互联网上的许多人审查过。

For more information on password storage schemes, read Jeff `s blog post: You're Probably Storing Passwords Incorrectly有关密码存储方案的更多信息,请阅读Jeff的博客文章:您可能错误地存储密码

Whatever you do if you go for the ' I'll do it myself, thank you ' approach, do not use MD5 or SHA1 anymore .无论你做什么,如果你采用“我自己做,谢谢”的方法,不要再使用MD5SHA1 They are nice hashing algorithm, but considered broken for security purposes .它们是不错的散列算法,但出于安全目的被认为已损坏

Currently, using crypt , with CRYPT_BLOWFISH is the best practice.目前,使用crypt和 CRYPT_BLOWFISH 是最佳实践。
CRYPT_BLOWFISH in PHP is an implementation of the Bcrypt hash. PHP 中的 CRYPT_BLOWFISH 是 Bcrypt 哈希的实现。 Bcrypt is based on the Blowfish block cipher, making use of it's expensive key setup to slow the algorithm down. Bcrypt 基于 Blowfish 分组密码,利用其昂贵的密钥设置来减慢算法速度。

Your users will be much safer if you used parameterized queries instead of concatenating SQL statements.如果您使用参数化查询而不是串联 SQL 语句,您的用户将更加安全。 And the salt should be unique for each user and should be stored along with the password hash.并且对于每个用户应该是唯一的,并且应该与密码哈希一起存储。

A better way would be for each user to have a unique salt.更好的方法是让每个用户都有一个独特的盐。

The benefit of having a salt is that it makes it harder for an attacker to pre-generate the MD5 signature of every dictionary word.使用 salt 的好处是它使攻击者更难预先生成每个字典单词的 MD5 签名。 But if an attacker learns that you have a fixed salt, they could then pre-generate the MD5 signature of every dictionary word prefixed by your fixed salt.但是,如果攻击者得知您有固定盐分,他们就可以预先生成以您的固定盐分为前缀的每个字典单词的 MD5 签名。

A better way is each time a user changes their password, your system generate a random salt and store that salt along with the user record.更好的方法是每次用户更改密码时,您的系统都会生成一个随机盐并将该盐与用户记录一起存储。 It makes it a bit more expensive to check the password (since you need to look up the salt before you can generate the MD5 signature) but it makes it much more difficult for an attacker to pre-generate MD5's.这使得检查密码的成本更高一些(因为您需要在生成 MD5 签名之前查找 salt),但它使攻击者预生成 MD5 变得更加困难。

With PHP 5.5 (what I describe is available to even earlier versions, see below) around the corner I'd like to suggest to use its new, built-in solution: password_hash() and password_verify() .随着 PHP 5.5(我所描述的甚至可用于更早版本,见下文)即将到来,我想建议使用其新的内置解决方案: password_hash()password_verify() It provides several options in order to achieve the level of password security you need (for example by specifying a "cost" parameter through the $options array)它提供了几个选项以达到您需要的密码安全级别(例如,通过$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));
?>

will return将返回

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

As you might see, the string contains the salt as well as the cost that was specified in the options.如您所见,该字符串包含盐以及选项中指定的成本。 It also contains the algorithm used.它还包含所使用的算法。

Therefore, when checking the password (for example when the user logs in), when using the complimentary password_verify() function it will extract the necessary crypto parameters from the password hash itself.因此,在检查密码时(例如当用户登录时),当使用免费的password_verify()函数时,它将从密码哈希本身中提取必要的加密参数。

When not specifying a salt, the generated password hash will be different upon every call of password_hash() because the salt is generated randomly.当不指定盐时,每次调用password_hash()时生成的密码哈希都会不同,因为盐是随机生成的。 Therefore comparing a previous hash with a newly generated one will fail, even for a correct password.因此,即使密码正确,将先前的哈希值与新生成的哈希值进行比较也会失败。

Verifying works like this:验证工作如下:

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.'));

I hope that providing these built-in functions will soon provide better password security in case of data theft, as it reduces the amount of thought the programmer has to put into a proper implementation.我希望提供这些内置函数将很快在数据被盗的情况下提供更好的密码安全性,因为它减少了程序员必须投入正确实现的思想量。

There is a small library (one PHP file) that will give you PHP 5.5's password_hash in PHP 5.3.7+: https://github.com/ircmaxell/password_compat有一个小型库(一个 PHP 文件)可以在 PHP 5.3.7+ 中为您提供 PHP 5.5 的password_hashhttps : //github.com/ircmaxell/password_compat

That's fine with me.这对我来说没问题。 Mr Atwood wrote about the strength of MD5 against rainbow tables , and basically with a long salt like that you're sitting pretty (though some random punctuation/numbers, it could improve it).阿特伍德先生写了关于MD5 对抗彩虹桌的强度,基本上用长盐就像你坐得很漂亮(虽然一些随机的标点符号/数字,它可以改善它)。

You could also look at SHA-1, which seems to be getting more popular these days.您还可以查看 SHA-1,它最近似乎越来越流行。

I want to add:我想补充:

  • Don't limit users passwords by length不要按长度限制用户密码

For compatibility with old systems often set a limit for the maximum length of the password.为了与旧系统兼容,通常会设置密码的最大长度限制。 This is a bad security policy: if you set restriction, set it only for the minimum length of passwords.这是一个糟糕的安全策略:如果您设置了限制,请仅针对密码的最小长度进行设置。

  • Don't send user passwords via email不要通过电子邮件发送用户密码

For recovering a forgotten password you should send the address by which user can change the password.要恢复忘记的密码,您应该发送用户可以更改密码的地址。

  • Update the hashes of users passwords更新用户密码的哈希值

The password hash may be out of date (parameters of the algorithm may be updated).密码哈希可能已过时(算法的参数可能会更新)。 By using the function password_needs_rehash() you can check it out.通过使用函数password_needs_rehash()你可以查看它。

如果有人在寻找此信息,我会在我的博客上写一篇有关该主题以及php定时攻击的文章: https : //fribba.com/the-secure-way-to-insert-a-password-into-database-与PHP /

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM