PHP 密码的安全散列和盐

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/401656/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 22:37:45  来源:igfitidea点击:

Secure hash and salt for PHP passwords

phpsecuritypasswordshashprotection

提问by luiscubal

It is currently said that MD5 is partially unsafe. Taking this into consideration, I'd like to know which mechanism to use for password protection.

目前据说 MD5 是部分不安全的。考虑到这一点,我想知道使用哪种机制来保护密码。

This question, Is “double hashing” a password less secure than just hashing it once?suggests that hashing multiple times may be a good idea, whereas How to implement password protection for individual files?suggests using salt.

这个问题,“双重散列”密码是否比仅散列一次更不安全?建议多次散列可能是一个好主意,而如何为单个文件实现密码保护?建议使用盐。

I'm using PHP. I want a safe and fast password encryption system. Hashing a password a million times may be safer, but also slower. How to achieve a good balance between speed and safety? Also, I'd prefer the result to have a constant number of characters.

我正在使用 PHP。我想要一个安全快速的密码加密系统。将密码散列一百万次可能更安全,但也更慢。如何在速度和安全之间取得良好的平衡?另外,我希望结果具有恒定数量的字符。

  1. The hashing mechanism must be available in PHP
  2. It must be safe
  3. It can use salt (in this case, are all salts equally good? Is there any way to generate good salts?)
  1. 散列机制必须在 PHP 中可用
  2. 一定是安全的
  3. 它可以使用盐(在这种情况下,所有盐都同样好?有什么方法可以产生好的盐吗?)

Also, should I store two fields in the database (one using MD5 and another one using SHA, for example)? Would it make it safer or unsafer?

另外,我是否应该在数据库中存储两个字段(例如,一个使用 MD5,另一个使用 SHA)?它会让它更安全还是更不安全?

In case I wasn't clear enough, I want to know which hashing function(s) to use and how to pick a good salt in order to have a safe and fast password protection mechanism.

如果我不够清楚,我想知道使用哪个散列函数以及如何选择一个好的盐以获得安全和快速的密码保护机制。

Related questions that don't quite cover my question:

不完全涵盖我的问题的相关问题:

What's the difference between SHA and MD5 in PHP
Simple Password Encryption
Secure methods of storing keys, passwords for asp.net
How would you implement salted passwords in Tomcat 5.5

PHP 中的 SHA 和 MD5 有什么区别
简单密码加密
存储密钥、asp.net密码的安全方法
你将如何在 Tomcat 5.5 中实现加盐密码

采纳答案by Robert K

DISCLAIMER: This answer was written in 2008.

Since then, PHP has given us password_hashand password_verifyand, since their introduction, they are the recommended password hashing & checking method.

The theory of the answer is still a good read though.

免责声明:这个答案是在 2008 年写的。

从那时起,PHP 给了我们password_hashpassword_verify而且,自从它们被引入以来,它们是推荐的密码散列和检查方法。

答案的理论仍然是一个很好的阅读。

TL;DR

TL; 博士

Don'ts

不要

  • Don't limit what characters users can enter for passwords. Only idiots do this.
  • Don't limit the length of a password. If your users want a sentence with supercalifragilisticexpialidocious in it, don't prevent them from using it.
  • Don't strip or escape HTML and special characters in the password.
  • Never store your user's password in plain-text.
  • Never email a password to your user except when they have lost theirs, and you sent a temporary one.
  • Never, ever log passwords in any manner.
  • Never hash passwords with SHA1or MD5 or even SHA256! Modern crackerscan exceed 60 and 180 billion hashes/second (respectively).
  • Don't mix bcrypt and with the rawoutput of hash(), either use hex output or base64_encode it. (This applies to any input that may have a rogue \0in it, which can seriously weaken security.)
  • 不要限制用户可以输入的密码字符。只有白痴才会这样做。
  • 不要限制密码的长度。如果你的用户想要一个带有 supercalifragilisticexpialidocious 的句子,不要阻止他们使用它。
  • 不要在密码中删除或转义 HTML 和特殊字符。
  • 永远不要以纯文本形式存储您的用户密码。
  • 永远不要通过电子邮件将密码发送给您的用户,除非他们丢失了密码并且您发送了一个临时密码
  • 永远不要以任何方式记录密码。
  • 永远不要使用SHA1或 MD5 甚至 SHA256散列密码!现代破解程序可以分别超过 60 和 1800 亿哈希/秒(分别)。
  • 不要将bcrypt 和hash()的原始输出混合使用,使用十六进制输出或 base64_encode 它。(这适用于任何可能包含流氓的输入\0,这会严重削弱安全性。)

Dos

DOS

  • Use scrypt when you can; bcrypt if you cannot.
  • Use PBKDF2 if you cannot use either bcrypt or scrypt, with SHA2 hashes.
  • Reset everyone's passwords when the database is compromised.
  • Implement a reasonable 8-10 character minimum length, plus require at least 1 upper case letter, 1 lower case letter, a number, and a symbol. This will improve the entropy of the password, in turn making it harder to crack. (See the "What makes a good password?" section for some debate.)
  • 尽可能使用 scrypt;如果不能,请使用 bcrypt。
  • 如果您不能使用 bcrypt 或 scrypt,请使用 PBKDF2 和 SHA2 哈希。
  • 当数据库受到威胁时重置每个人的密码。
  • 实现合理的 8-10 个字符的最小长度,此外还需要至少 1 个大写字母、1 个小写字母、一个数字和一个符号。这将提高密码的熵,从而使其更难破解。(有关一些辩论,请参阅“什么是好的密码?”部分。)

Why hash passwords anyway?

为什么要散列密码?

The objective behind hashing passwords is simple: preventing malicious access to user accounts by compromising the database. So the goal of password hashing is to deter a hacker or cracker by costing them too much time or money to calculate the plain-text passwords. And time/cost are the best deterrents in your arsenal.

散列密码背后的目标很简单:通过破坏数据库来防止对用户帐户的恶意访问。因此,密码散列的目标是通过花费太多时间或金钱来计算纯文本密码来阻止黑客或破解者。时间/成本是您武器库中最好的威慑力量。

Another reason that you want a good, robust hash on a user accounts is to give you enough time to change all the passwords in the system. If your database is compromised you will need enough time to at leastlock the system down, if not change every password in the database.

您需要对用户帐户进行良好、健壮的散列的另一个原因是给您足够的时间来更改系统中的所有密码。如果您的数据库遭到破坏,您将需要足够的时间至少将系统锁定,如果不是更改数据库中的每个密码。

Jeremiah Grossman, CTO of Whitehat Security, stated on White Hat Security blogafter a recent password recovery that required brute-force breaking of his password protection:

Whitehat Security 的 CTO Jeremiah Grossman在最近一次密码恢复后在 White Hat Security 博客上表示,该密码恢复需要暴力破解密码保护:

Interestingly, in living out this nightmare, I learned A LOT I didn't know about password cracking, storage, and complexity. I've come to appreciate why password storage is ever so much more important than password complexity. If you don't know how your password is stored, then all you really can depend upon is complexity.This might be common knowledge to password and crypto pros, but for the average InfoSec or Web Security expert, I highly doubt it.

有趣的是,在经历这个噩梦的过程中,我学到了很多我不知道的密码破解、存储和复杂性。我开始理解为什么密码存储比密码复杂性更重要。如果您不知道密码是如何存储的,那么您真正可以依赖的就是复杂性。这可能是密码和加密专业人士的常识,但对于普通的信息安全或网络安全专家来说,我非常怀疑。

(Emphasis mine.)

(强调我的。)

What makes a goodpassword anyway?

什么才是好的密码?

Entropy. (Not that I fully subscribe to Randall's viewpoint.)

。(并不是说我完全赞同兰德尔的观点。)

In short, entropy is how much variation is within the password. When a password is only lowercase roman letters, that's only 26 characters. That isn't much variation. Alpha-numeric passwords are better, with 36 characters. But allowing upper and lower case, with symbols, is roughly 96 characters. That's a lot better than just letters. One problem is, to make our passwords memorable we insert patterns—which reduces entropy. Oops!

简而言之,熵是密码内的变化量。当密码仅为小写罗马字母时,即只有 26 个字符。那变化不大。字母数字密码更好,有 36 个字符。但允许大小写,带符号,大约是 96 个字符。这比只是字母要好得多。一个问题是,为了让我们的密码令人难忘,我们插入了模式——这减少了熵。哎呀!

Password entropy is approximatedeasily. Using the full range of ascii characters (roughly 96 typeable characters) yields an entropy of 6.6 per character, which at 8 characters for a password is still too low (52.679 bits of entropy) for future security. But the good news is: longer passwords, and passwords with unicode characters, really increase the entropy of a password and make it harder to crack.

密码熵很容易近似。使用全范围的 ascii 字符(大约 96 个可键入的字符)产生每个字符 6.6 的熵,对于未来的安全来说,8 个字符的密码仍然太低(52.679 位的熵)。但好消息是:更长的密码,以及带有 unicode 字符的密码,确实会增加密码的熵并使其更难破解。

There's a longer discussion of password entropy on the Crypto StackExchangesite. A good Google search will also turn up a lot of results.

Crypto StackExchange站点上有更长的关于密码熵的讨论。一个好的谷歌搜索也会出现很多结果。

In the comments I talked with @popnoodles, who pointed out that enforcinga password policy of X length with X many letters, numbers, symbols, etc, can actually reduce entropy by making the password scheme more predictable. I do agree. Randomess, as truly random as possible, is always the safest but least memorable solution.

在我与@popnoodles 交谈的评论中,他指出执行X 长度的密码策略,其中包含 X 个字母、数字、符号等,实际上可以通过使密码方案更可预测来减少熵。我同意。随机性,尽可能真正的随机性,始终是最安全但最不令人难忘的解决方案。

So far as I've been able to tell, making the world's best password is a Catch-22. Either its not memorable, too predictable, too short, too many unicode characters (hard to type on a Windows/Mobile device), too long, etc. No password is truly good enough for our purposes, so we must protect them as though they were in Fort Knox.

据我所知,制作世界上最好的密码是 Catch-22。要么它不令人难忘、太可预测、太短、太多 unicode 字符(在 Windows/Mobile 设备上难以输入)、太长等等。没有密码对我们的目的来说真的足够好,所以我们必须像保护它们一样保护它们在诺克斯堡。

Best practices

最佳实践

Bcrypt and scryptare the current best practices. Scryptwill be better than bcrypt in time, but it hasn't seen adoption as a standard by Linux/Unix or by webservers, and hasn't had in-depth reviews of its algorithm posted yet. But still, the future of the algorithm does look promising. If you are working with Ruby there is an scrypt gemthat will help you out, and Node.js now has its own scryptpackage. You can use Scrypt in PHP either via the Scryptextension or the Libsodiumextension (both are available in PECL).

Bcrypt 和scrypt是当前的最佳实践。Scrypt将比 bcrypt 更好,但它尚未被 Linux/Unix 或网络服务器视为标准,并且尚未发布对其算法的深入评论。但是,该算法的未来看起来确实很有希望。如果您使用 Ruby,有一个scrypt gem可以帮助您,Node.js 现在有自己的scrypt包。您可以通过Scrypt扩展或Libsodium扩展(两者都在 PECL 中可用)在 PHP 中使用 Scrypt 。

I highly suggest reading the documentation for the crypt functionif you want to understand how to use bcrypt, or finding yourself a goodwrapperor use something like PHPASSfor a more legacy implementation. I recommend a minimum of 12 rounds of bcrypt, if not 15 to 18.

我强烈建议您阅读crypt 函数的文档,如果您想了解如何使用 bcrypt,或者找到一个好的包装器或使用PHPASS 之类的东西来实现更旧的实现。我建议至少 12 轮 bcrypt,如果不是 15 到 18 轮。

I changed my mind about using bcrypt when I learned that bcrypt only uses blowfish's key schedule, with a variable cost mechanism. The latter lets you increase the cost to brute-force a password by increasing blowfish's already expensive key schedule.

当我了解到 bcrypt 只使用河豚的密钥时间表,并且具有可变成本机制时,我改变了使用 bcrypt 的想法。后者让您通过增加河豚已经很昂贵的密钥时间表来增加暴力破解密码的成本。

Average practices

平均做法

I almost can't imagine this situation anymore. PHPASSsupports PHP 3.0.18 through 5.3, so it is usable on almost every installation imaginable—and should be used if you don't know for certainthat your environment supports bcrypt.

我几乎无法想象这种情况了。PHPASS支持 PHP 3.0.18 到 5.3,因此它几乎可以用于所有可以想象的安装——如果您不确定您的环境是否支持 bcrypt ,应该使用它。

But suppose that you cannot use bcrypt or PHPASS at all. What then?

但是假设您根本不能使用 bcrypt 或 PHPASS。然后怎样呢?

Try an implementation of PDKBF2with the maximum number of roundsthat your environment/application/user-perception can tolerate. The lowest number I'd recommend is 2500 rounds. Also, make sure to use hash_hmac()if it is available to make the operation harder to reproduce.

尝试使用您的环境/应用程序/用户感知可以容忍的最大轮数实现PDKBF2。我建议的最低数量是 2500 轮。此外,请确保使用hash_hmac()如果它可以使操作更难重现。

Future Practices

未来实践

Coming in PHP 5.5 is a full password protection librarythat abstracts away any pains of working with bcrypt. While most of us are stuck with PHP 5.2 and 5.3 in most common environments, especially shared hosts, @ircmaxell has built a compatibility layerfor the coming API that is backward compatible to PHP 5.3.7.

PHP 5.5 是一个完整的密码保护库,可以消除使用 bcrypt 的任何痛苦。虽然我们大多数人在大多数常见环境中都坚持使用 PHP 5.2 和 5.3,尤其是共享主机,但@ircmaxell为即将推出的 API构建了一个兼容层,该向后兼容 PHP 5.3.7。

Cryptography Recap & Disclaimer

密码学回顾和免责声明

The computational power required to actually cracka hashed password doesn't exist. The only way for computers to "crack" a password is to recreate it and simulate the hashing algorithm used to secure it. The speed of the hash is linearly related to its ability to be brute-forced. Worse still, most hash algorithms can be easily parallelized to perform even faster. This is why costly schemes like bcrypt and scrypt are so important.

实际破解散列密码所需的计算能力并不存在。计算机“破解”密码的唯一方法是重新创建密码并模拟用于保护密码的散列算法。散列的速度与其暴力破解的能力呈线性关系。更糟糕的是,大多数哈希算法都可以轻松并行化以更快地执行。这就是为什么像 bcrypt 和 scrypt 这样昂贵的方案如此重要的原因。

You cannot possibly foresee all threats or avenues of attack, and so you must make your best effort to protect your users up front. If you do not, then you might even miss the fact that you were attacked until it's too late... and you're liable. To avoid that situation, act paranoid to begin with. Attack your own software (internally) and attempt to steal user credentials, or modify other user's accounts or access their data. If you don't test the security of your system, then you cannot blame anyone but yourself.

你不可能预见攻击的所有威胁或途径,所以你必须保护用户你最大的努力了前面。如果你不这样做,那么你甚至可能会错过你被攻击的事实,直到为时已晚......而且你有责任。为了避免这种情况,首先要表现出偏执。攻击您自己的软件(内部)并尝试窃取用户凭据,或修改其他用户的帐户或访问他们的数据。如果您不测试系统的安全性,那么除了您自己之外,您不能责怪任何人。

Lastly: I am not a cryptographer. Whatever I've said is my opinion, but I happen to think it's based on good ol' common sense ... and lots of reading. Remember, be as paranoid as possible, make things as hard to intrude as possible, and then, if you are still worried, contact a white-hat hacker or cryptographer to see what they say about your code/system.

最后:我不是密码学家。我所说的都是我的观点,但我碰巧认为它是基于良好的常识......以及大量阅读。请记住,尽可能多疑,让事情尽可能难以侵入,然后,如果您仍然担心,请联系白帽黑客或密码学家,看看他们对您的代码/系统有何看法。

回答by RichVel

A much shorter and safer answer - don't write your own password mechanism at all, use a tried and tested mechanism.

一个更短更安全的答案 -根本不要编写自己的密码机制,使用久经考验的机制。

  • PHP 5.5 or higher: password_hash()is good quality and part of PHP core.
  • Older PHP versions: OpenWall's phpasslibrary is much better than most custom code - used in WordPress, Drupal, etc.
  • PHP 5.5 或更高版本:password_hash()质量很好,是 PHP 核心的一部分。
  • 较旧的 PHP 版本:OpenWall 的phpass库比大多数自定义代码要好得多 - 在 WordPress、Drupal 等中使用。

Most programmers just don't have the expertise to write crypto related code safely without introducing vulnerabilities.

大多数程序员只是不具备在不引入漏洞的情况下安全编写加密相关代码的专业知识。

Quick self-test:what is password stretching and how many iterations should you use? If you don't know the answer, you should use password_hash(), as password stretching is now a critical feature of password mechanisms due to much faster CPUs and the use of GPUs and FPGAsto crack passwords at rates of billions of guesses per second(with GPUs).

快速自测:什么是密码拉伸以及应该使用多少次迭代?如果你不知道答案,你应该使用password_hash(),因为密码拉伸现在是密码机制的一个关键特性,因为 CPU 速度更快,并且使用GPU 和 FPGA每秒数十亿次猜测的速度破解密码(使用 GPU )。

For example, you can crack all 8-character Windows passwords in 6 hoursusing 25 GPUs installed in 5 desktop PCs. This is brute-forcing i.e. enumerating and checking every 8-character Windows password, including special characters, and is not a dictionary attack. That was in 2012, as of 2018 you could use fewer GPUs, or crack faster with 25 GPUs.

例如,使用安装在 5 台台式机上的 25 个 GPU ,您可以在 6 小时内破解所有 8 个字符的 Windows 密码。这是蛮力,即枚举和检查每个 8 个字符的 Windows 密码,包括特殊字符,而不是字典攻击。那是在 2012 年,到 2018 年,您可以使用更少的 GPU,或者使用 25 个 GPU 更快地破解。

There are also many rainbow table attacks on Windows passwords that run on ordinary CPUs and are very fast. All this is because Windows stilldoesn't salt or stretchits passwords, even in Windows 10- don't make the same mistake as Microsoft did!

在普通CPU上运行的Windows密码也有很多彩虹表攻击,速度非常快。所有这一切都是因为即使在 Windows 10 中,Windows仍然不会加盐或延长其密码- 不要像微软那样犯同样的错误!

See also:

也可以看看:

  • excellent answerwith more about why password_hash()or phpassare the best way to go.
  • good blog articlegiving recommmended 'work factors' (number of iterations) for main algorithms including bcrypt, scrypt and PBKDF2.
  • 很好的答案,更多地了解为什么password_hash()或是phpass最好的方法。
  • 很好的博客文章,为包括 bcrypt、scrypt 和 PBKDF2 在内的主要算法提供了推荐的“工作因素”(迭代次数)。

回答by Tom Haigh

I would not store the password hashed in two different ways, because then the system is at least as weak as the weakest of the hash algorithms in use.

我不会以两种不同的方式存储散列的密码,因为这样系统至少与使用中最弱的散列算法一样弱。

回答by AlliterativeAlice

As of PHP 5.5, PHP has simple, secure functions for hashing and verifying passwords, password_hash()and password_verify()

从 PHP 5.5 开始,PHP 具有用于散列和验证密码的简单、安全的函数,password_hash()password_verify()

$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

When password_hash()is used, it generates a random salt and includes it in the outputted hash (along with the the cost and algorithm used.) password_verify()then reads that hash and determines the salt and encryption method used, and verifies it against the provided plaintext password.

password_hash()被使用时,它产生一个随机盐和其包括在所输出的哈希值(与成本和使用的算法。沿)password_verify()然后读取该散列并确定使用的盐和加密方法,并且验证它靠在提供明文口令。

Providing the PASSWORD_DEFAULTinstructs PHP to use the default hashing algorithm of the installed version of PHP. Exactly which algorithm that means is intended to change over time in future versions, so that it will always be one of the strongest available algorithms.

提供PASSWORD_DEFAULT指示 PHP 使用已安装 PHP 版本的默认散列算法。确切地说,这意味着哪种算法会在未来版本中随着时间的推移而改变,以便它始终是最强大的可用算法之一。

Increasing cost (which defaults to 10) makes the hash harder to brute-force but also means generating hashes and verifying passwords against them will be more work for your server's CPU.

增加的成本(默认为 10)使散列更难暴力破解,但也意味着生成散列并针对它们验证密码将为您的服务器 CPU 做更多的工作。

Note that even though the default hashing algorithm may change, old hashes will continue to verify just fine because the algorithm used is stored in the hash and password_verify()picks up on it.

请注意,即使默认的散列算法可能会改变,旧的散列将继续验证得很好,因为使用的算法存储在散列中并对其进行password_verify()提取。

回答by Gaurav Kumar

Though the question has been answered, I just want to reiterate that salts used for hashing should be random and not like email address as suggested in first answer.

尽管问题已得到解答,但我只想重申,用于散列的盐应该是随机的,而不是像第一个答案中建议的电子邮件地址。

More explanation is available at- http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

更多解释可在- http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

Recently I had a discussion whether password hashes salted with random bits are more secure than the one salted with guessable or known salts. Let's see: If the system storing password is compromised as well as the system which stores the random salt, the attacker will have access to hash as well as salt, so whether the salt is random or not, doesn't matter. The attacker will can generate pre-computed rainbow tables to crack the hash. Here comes the interesting part- it is not so trivial to generate pre-computed tables. Let us take example of WPA security model. Your WPA password is actually never sent to Wireless Access Point. Instead, it is hashed with your SSID (the network name- like Linksys, Dlink etc). A very good explanation of how this works is here. In order to retrieve password from hash, you will need to know the password as well as salt (network name). Church of Wifi has already pre-computed hash tables which has top 1000 SSIDs and about 1 million passwords. The size is of all tables is about 40 GB. As you can read on their site, someone used 15 FGPA arrays for 3 days to generate these tables. Assuming victim is using the SSID as “a387csf3″ and password as “123456″, will it be cracked by those tables? No! .. it cannot. Even if the password is weak, the tables don't have hashes for SSID a387csf3. This is the beauty of having random salt. It will deter crackers who thrive upon pre-computed tables. Can it stop a determined hacker? Probably not. But using random salts does provide additional layer of defense. While we are on this topic, let us discuss additional advantage of storing random salts on a separate system. Scenario #1 : Password hashes are stored on system X and salt values used for hashing are stored on system Y. These salt values are guessable or known (e.g. username) Scenario#2 : Password hashes are stored on system X and salt values used for hashing are stored on system Y. These salt values are random. In case system X has been compromised, as you can guess, there is a huge advantage of using random salt on a separate system (Scenario #2) . The attacker will need to guess addition values to be able to crack hashes. If a 32 bit salt is used, 2^32= 4,294,967,296 (about 4.2 billion) iterations will can be required for each password guessed.

最近我讨论了用随机位腌制的密码哈希是否比用可猜测或已知的盐腌制的密码哈希更安全。让我们看看:如果存储密码的系统以及存储随机盐的系统遭到破坏,攻击者将可以访问散列和盐,因此盐是否是随机的并不重要。攻击者可以生成预先计算好的彩虹表来破解哈希。有趣的部分来了——生成预先计算的表格并不是那么简单。让我们以 WPA 安全模型为例。您的 WPA 密码实际上从未发送到无线接入点。相反,它使用您的 SSID(网络名称,如 Linksys、Dlink 等)进行散列。关于这是如何工作的一个很好的解释是here。为了从哈希中检索密码,您将需要知道密码以及 salt(网络名称)。Church of Wifi 已经预先计算了具有前 1000 个 SSID 和大约 100 万个密码的哈希表。所有表的大小约为 40 GB。正如您在他们的网站上看到的那样,有人使用 15 个 FGPA 阵列 3 天来生成这些表。假设受害者使用的SSID为“a387csf3”,密码为“123456”,这些表会破解吗?不!.. 这不可以。即使密码很弱,这些表也没有 SSID a387csf3 的哈希值。这就是随机盐的美妙之处。它将阻止那些在预先计算的表格上茁壮成长的饼干。它可以阻止一个坚定的黑客吗?可能不是。但是使用随机盐确实提供了额外的防御层。当我们讨论这个话题时,让我们讨论在单独的系统上存储随机盐的额外优势。场景#1:密码散列存储在系统 X 上,用于散列的盐值存储在系统 Y 上。这些盐值是可猜测的或已知的(例如用户名) 场景#2:密码散列存储在系统 X 上,盐值用于散列存储在系统 Y 上。这些盐值是随机的。如果系统 X 遭到破坏,您可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测加法值才能破解哈希。如果使用 32 位 salt,则每个猜测的密码可能需要 2^32=4,294,967,296(约 42 亿)次迭代。密码散列存储在系统 X 上,用于散列的盐值存储在系统 Y 上。这些盐值是可猜测的或已知的(例如用户名) 场景#2:密码散列存储在系统 X 上,用于散列的盐值存储在系统 Y。这些盐值是随机的。如果系统 X 遭到破坏,您可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测加法值才能破解哈希。如果使用 32 位 salt,则每个猜测的密码可能需要 2^32=4,294,967,296(约 42 亿)次迭代。密码散列存储在系统 X 上,用于散列的盐值存储在系统 Y 上。这些盐值是可猜测的或已知的(例如用户名) 场景#2:密码散列存储在系统 X 上,用于散列的盐值存储在系统 Y。这些盐值是随机的。如果系统 X 遭到破坏,您可以猜到,在单独的系统上使用随机盐会带来巨大的优势(场景 #2)。攻击者需要猜测加法值才能破解哈希。如果使用 32 位 salt,则每个猜测的密码可能需要 2^32=4,294,967,296(约 42 亿)次迭代。这些盐值是随机的。如果系统 X 遭到破坏,您可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测加法值才能破解哈希。如果使用 32 位 salt,则每个猜测的密码可能需要 2^32=4,294,967,296(约 42 亿)次迭代。这些盐值是随机的。如果系统 X 遭到破坏,您可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测加法值才能破解哈希。如果使用 32 位 salt,则每个猜测的密码可能需要 2^32=4,294,967,296(约 42 亿)次迭代。

回答by JonoCoetzee

I just want to point out that PHP 5.5 includes a password hashing APIthat provides a wrapper around crypt(). This API significantly simplifies the task of hashing, verifying and rehashing password hashes. The author has also released a compatibility pack(in the form of a single password.php file that you simply requireto use), for those using PHP 5.3.7 and later and want to use this right now.

我只想指出 PHP 5.5 包含一个密码散列 API,它提供了crypt(). 此 API 显着简化了散列、验证和重新散列密码散列的任务。作者还发布了一个兼容包(以单个 password.php 文件的形式,您只需require使用),供那些使用 PHP 5.3.7 及更高版本并希望立即使用的人使用。

It only supports BCRYPT for now, but it aims to be easily extended to include other password hashing techniques and because the technique and cost is stored as part of the hash, changes to your prefered hashing technique/cost will not invalidate current hashes, the framework will automagically, use the correct technique/cost when validating. It also handles generating a "secure" salt if you do not explicitly define your own.

它目前仅支持 BCRYPT,但它旨在轻松扩展以包含其他密码散列技术,并且由于技术和成本作为散列的一部分存储,因此更改您首选的散列技术/成本不会使当前的散列无效,框架验证时将自动使用正确的技术/成本。如果您没有明确定义自己的盐,它还可以处理生成“安全”盐。

The API exposes four functions:

API 公开了四个函数:

  • password_get_info()- returns information about the given hash
  • password_hash()- creates a password hash
  • password_needs_rehash()- checks if the given hash matches the given options. Useful to check if the hash conforms to your current technique/cost scheme allowing you to rehash if necessary
  • password_verify()- verifies that a password matches a hash
  • password_get_info()- 返回有关给定哈希的信息
  • password_hash()- 创建密码哈希
  • password_needs_rehash()- 检查给定的哈希值是否与给定的选项匹配。用于检查散列是否符合您当前的技术/成本方案,允许您在必要时重新散列
  • password_verify()- 验证密码是否与哈希匹配

At the moment these functions accept the PASSWORD_BCRYPT and PASSWORD_DEFAULT password constants, which are synonymous at the moment, the difference being that PASSWORD_DEFAULT "may change in newer PHP releases when newer, stronger hashing algorithms are supported." Using PASSWORD_DEFAULT and password_needs_rehash() on login (and rehashing if necessary) should ensure that your hashes are reasonably resilient to brute-force attacks with little to no work for you.

目前,这些函数接受 PASSWORD_BCRYPT 和 PASSWORD_DEFAULT 密码常量,它们目前是同义词,不同之处在于 PASSWORD_DEFAULT “当支持更新、更强的散列算法时,可能会在更新的 PHP 版本中更改。” 在登录时使用 PASSWORD_DEFAULT 和 password_needs_rehash()(并在必要时重新散列)应确保您的散列对蛮力攻击具有合理的弹性,而您几乎没有工作。

EDIT: I just realised that this is mentioned briefly in Robert K's answer. I'll leave this answer here since I think it provides a bit more information about how it works and the ease of use it provides for those who don't know security.

编辑:我刚刚意识到罗伯特 K 的回答中简要提到了这一点。我将这个答案留在这里,因为我认为它提供了更多有关它的工作原理以及它为不了解安全性的人提供的易用性的信息。

回答by rabudde

I'm using Phpasswhich is a simple one-file PHP class that could be implemented very easily in nearly every PHP project. See also The H.

我正在使用Phpass,它是一个简单的单文件 PHP 类,几乎可以在每个 PHP 项目中轻松实现。另请参见第h

By default it used strongest available encryption that is implemented in Phpass, which is bcryptand falls back to other encryptions down to MD5 to provide backward compatibility to frameworks like Wordpress.

默认情况下,它使用在 Phpass 中实现的最强大的可用加密,这是bcrypt并回退到其他加密到 MD5,以提供对 Wordpress 等框架的向后兼容性。

The returned hash could be stored in database as it is. Sample use for generating hash is:

返回的散列可以按原样存储在数据库中。生成哈希的示例用途是:

$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);

To verify password, one can use:

要验证密码,可以使用:

$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);

回答by wmfrancia

THINGS TO REMEMBER

需要记住的事情

A lot has been said about Password encryption for PHP, most of which is very good advice, but before you even start the process of using PHP for password encryption make sure you have the following implemented or ready to be implemented.

关于 PHP 的密码加密已经说了很多,其中大部分都是非常好的建议,但在您开始使用 PHP 进行密码加密的过程之前,请确保您已实施或准备实施以下内容。

SERVER

服务器

PORTS

港口

No matter how good your encryption is if you don't properly secure the server that runs the PHP and DB all your efforts are worthless. Most servers function relatively the same way, they have ports assigned to allow you to access them remotely either through ftp or shell. Make sure that you change the default port of which ever remote connection you have active. By not doing this you in effect have made the attacker do one less step in accessing your system.

无论您的加密有多好,如果您没有正确保护运行 PHP 和 DB 的服务器,您的所有努力都将一文不值。大多数服务器的功能相对相同,它们分配了端口,允许您通过 ftp 或 shell 远程访问它们。确保更改您已激活的远程连接的默认端口。通过不这样做,您实际上使攻击者在访问您的系统时少了一步。

USERNAME

用户名

For all that is good in the world do not use the username admin, root or something similar. Also if you are on a unix based system DO NOT make the root account login accessible, it should always be sudo only.

对于世界上所有好的东西,不要使用用户名 admin、root 或类似的东西。此外,如果您使用的是基于 unix 的系统,请不要让 root 帐户登录可访问,它应该始终只有 sudo。

PASSWORD

密码

You tell your users to make good passwords to avoid getting hacked, do the same. What is the point in going through all the effort of locking your front door when you have the backdoor wide open.

你告诉你的用户设置好密码以避免被黑客攻击,做同样的事情。当后门大开时,费尽心思锁住前门又有什么意义呢?

DATABASE

数据库

SERVER

服务器

Ideally you want your DB and APPLICATION on separate servers. This is not always possible due to cost, but it does allow for some safety as the attacker will have to go through two steps to fully access the system.

理想情况下,您希望您的 DB 和 APPLICATION 位于不同的服务器上。由于成本原因,这并不总是可行,但它确实考虑到了一些安全性,因为攻击者必须通过两个步骤才能完全访问系统。

USER

用户

Always have your application have its own account to access the DB, and only give it the privileges it will need.

始终让您的应用程序拥有自己的帐户来访问数据库,并且只为其提供所需的权限。

Then have a separate user account for you that is not stored anywhere on the server, not even in the application.

然后为您创建一个单独的用户帐户,该帐户不会存储在服务器的任何位置,甚至不会存储在应用程序中。

Like always DO NOT make this root or something similar.

就像总是不要做这个根或类似的东西。

PASSWORD

密码

Follow the same guidelines as with all good passwords. Also don't reuse the same password on any SERVER or DB accounts on the same system.

遵循与所有好的密码相同的准则。也不要在同一系统上的任何 SERVER 或 DB 帐户上重复使用相同的密码。

PHP

PHP

PASSWORD

密码

NEVER EVER store a password in your DB, instead store the hash and unique salt, I will explain why later.

永远不要在你的数据库中存储密码,而是存储哈希和唯一的盐,我稍后会解释原因。

HASHING

哈希

ONE WAY HASHING!!!!!!!, Never hash a password in a way that it can be reversed, Hashes should be one way, meaning you don't reverse them and compare them to the password, you instead hash the entered password the same way and compare the two hashes. This means that even if an attacker gets access to the DB he doesn't know what the actually password is, just its resulting hash. Which means more security for your users in the worst possible scenario.

ONE WAY HASHING!!!!!!!, 永远不要以可以反转的方式对密码进行散列,散列应该是一种方式,这意味着您不会反转它们并将它们与密码进行比较,而是对输入的密码进行散列以相同的方式比较两个哈希值。这意味着即使攻击者可以访问数据库,他也不知道实际密码是什么,只知道其结果哈希。这意味着在最糟糕的情况下为您的用户提供更高的安全性。

There are a lot of good hashing functions out there (password_hash, hash, etc...) but you need to select a good algorithm for the hash to be effective. (bcrypt and ones similar to it are decent algorithms.)

有很多很好的散列函数(password_hash,,hash等等...)但是你需要选择一个好的算法才能使散列有效。(bcrypt 和类似的都是不错的算法。)

When hashing speed is the key, the slower the more resistant to Brute Force attacks.

当哈希速度是关键时,越慢越能抵抗蛮力攻击。

One of the most common mistakes in hashing is that hashes are not unique to the users. This is mainly because salts are not uniquely generated.

散列中最常见的错误之一是散列不是用户独有的。这主要是因为盐不是唯一生成的。

SALTING

Passwords should always be salted before hashed. Salting adds a random string to the password so similar passwords don't appear the same in the DB. However if the salt is not unique to each user (ie: you use a hard coded salt) than you pretty much have made your salt worthless. Because once an attacker figures out one password salt he has the salt for all of them.

密码在散列之前应该总是加盐。Salting 会向密码中添加一个随机字符串,因此类似的密码在数据库中不会出现相同的情况。但是,如果盐不是每个用户独有的(即:您使用硬编码的盐),那么您的盐就几乎一文不值。因为一旦攻击者找出一个密码盐,他就拥有了所有密码盐。

When you create a salt make sure it is unique to the password it is salting, then store both the completed hash and salt in your DB. What this will do is make it so that an attacker will have to individually crack each salt and hash before they can gain access. This means a lot more work and time for the attacker.

当你创建一个 salt 时,确保它对于它正在加盐的密码是唯一的,然后将完成的哈希和 salt 存储在你的数据库中。这样做的目的是让攻击者在获得访问权限之前必须单独破解每个 salt 和 hash。这意味着攻击者需要更多的工作和时间。

USERS CREATING PASSWORDS

用户创建密码

If the user is creating a password through the frontend that means it has to be sent to the server. This opens up a security issue because that means the unencrypted password is being sent to the server and if a attacker is able to listen and access that all your security in PHP is worthless. ALWAYS transmit the data SECURELY, this is done through SSL, but be weary even SSL is not flawless (OpenSSL's Heartbleed flaw is an example of this).

如果用户通过前端创建密码,则意味着必须将其发送到服务器。这会带来一个安全问题,因为这意味着未加密的密码将被发送到服务器,如果攻击者能够监听和访问 PHP,那么您在 PHP 中的所有安全性都是毫无价值的。始终安全地传输数据,这是通过 SSL 完成的,但是即使 SSL 也不是完美无缺的(OpenSSL 的 Heartbleed 缺陷就是一个例子),请注意。

Also make the user create a secure password, it is simple and should always be done, the user will be grateful for it in the end.

也让用户创建一个安全的密码,这很简单,应该经常做,用户最终会感激的。

Finally, no matter the security measures you take nothing is 100% secure, the more advanced the technology to protect becomes the more advanced the attacks become. But following these steps will make your site more secure and far less desirable for attackers to go after.

最后,无论您采取的安全措施是 100% 安全,保护技术越先进,攻击就越先进。但遵循这些步骤将使您的网站更加安全,并且更不适合攻击者追踪。

Here is a PHP class that creates a hash and salt for a password easily

这是一个 PHP 类,它可以轻松地为密码创建哈希值和盐值

http://git.io/mSJqpw

http://git.io/mSJqpw

回答by AticusFinch

Google says SHA256 is available to PHP.

谷歌表示 SHA256 可用于 PHP。

You should definitely use a salt. I'd recommend using random bytes (and not restrict yourself to characters and numbers). As usually, the longer you choose, the safer, slower it gets. 64 bytes ought to be fine, i guess.

你绝对应该使用盐。我建议使用随机字节(而不是将自己限制为字符和数字)。通常,您选择的时间越长,就越安全,速度越慢。64 字节应该没问题,我猜。

回答by Max

In the end, double-hashing, mathematically, provides no benefit. In practice, however, it is useful for preventing rainbow table-based attacks. In other words, it is of no more benefit than hashing with a salt, which takes far less processor time in your application or on your server.

最后,从数学上讲,双重散列没有任何好处。然而,在实践中,它对于防止基于彩虹表的攻击很有用。换句话说,它没有比使用盐进行散列更多的好处,它在您的应用程序或服务器上花费的处理器时间要少得多。