[英]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.因为安全性往往比大多数程序员单独解决的要复杂得多,而且有更多不可见的搞砸可能性,所以使用标准库几乎总是最简单和最安全(如果不是唯一的话)的可用选项。
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以访问内置函数)
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';
}
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 已经在一些非常著名的项目中实现:
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 .无论你做什么,如果你采用“我自己做,谢谢”的方法,不要再使用MD5
或SHA1
。 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 分组密码,利用其昂贵的密钥设置来减慢算法速度。
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_hash
: https : //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:我想补充:
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.这是一个糟糕的安全策略:如果您设置了限制,请仅针对密码的最小长度进行设置。
For recovering a forgotten password you should send the address by which user can change the password.要恢复忘记的密码,您应该发送用户可以更改密码的地址。
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.