安全的哈希和盐的PHP密码。

时间:2022-12-13 12:49:53

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. 哈希机制必须在PHP中可用。
  3. It must be safe
  4. 它必须是安全的
  5. It can use salt (in this case, are all salts equally good? Is there any way to generate good salts?)
  6. 它可以用盐(在这种情况下,所有的盐类都一样好吗?有没有办法产生好的盐?)

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的区别是什么?简单的密码加密安全方法存储密钥,net的密码如何在Tomcat 5.5中实现加密密码!

14 个解决方案

#1


871  

DISCLAIMER: This answer was written in 2008.

免责声明:这个答案写于2008年。

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

从那以后,PHP给我们提供了password_hash和password_verify,并且由于它们的介绍,它们是推荐的密码散列和检查方法。

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

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

TL;DR

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.
  • 不要限制密码的长度。如果你的用户想要一个具有supercalifragilisticexpialidocious的句子,不要阻止他们使用它。
  • 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 SHA1 or MD5 or even SHA256! Modern crackers can exceed 60 and 180 billion hashes/second (respectively).
  • 不要使用SHA1或MD5甚至SHA256来哈希密码!现代的饼干可以超过60和1800亿哈希/秒(分别)。
  • Don't mix bcrypt and with the raw output of hash(), either use hex output or base64_encode it. (This applies to any input that may have a rogue \0 in it, which can seriously weaken security.)
  • 不要混合bcrypt和散列()的原始输出,要么使用十六进制输出,要么使用base64_encode。(这适用于任何可能有流氓\0的输入,这将严重削弱安全性。)

Dos

  • Use scrypt when you can; bcrypt if you cannot.
  • 当你可以的时候使用scrypt;如果你不能bcrypt。
  • Use PBKDF2 if you cannot use either bcrypt or scrypt, with SHA2 hashes.
  • 如果您不能使用bcrypt或scrypt,使用SHA2散列,请使用PBKDF2。
  • 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.)
  • 实现一个合理的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 least lock the system down, if not change every password in the database.

你想要一个好的、健壮的用户帐户哈希的另一个原因是给你足够的时间来修改系统中的所有密码。如果您的数据库被破坏,您将需要足够的时间来至少锁定系统,如果不改变数据库中的每个密码。

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

杰里迈亚·格鲁斯曼(Jeremiah Grossman)是Whitehat 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 good password 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 approximated easily. 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 StackExchange site. A good Google search will also turn up a lot of results.

在加密交换站点上有一个关于密码熵的更长的讨论。一个好的谷歌搜索也会有很多的结果。

In the comments I talked with @popnoodles, who pointed out that enforcing a 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长度的密码策略(如字母、数字、符号等),可以通过使密码方案更可预测来减少熵。我同意。随机,尽可能的随机,总是最安全,但最不容易记住的解决方案。

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.

就我所知,让世界上最好的密码是第22条。它不是令人难忘的,太容易预测,太短,太多的unicode字符(很难在Windows/移动设备上打字),太长,等等。我们的目的没有密码,所以我们必须保护他们,就像他们在诺克斯堡一样。

Best practices

Bcrypt and scrypt are the current best practices. Scrypt will 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 gem that will help you out, and Node.js now has its own scrypt package. You can use Scrypt in PHP either via the Scrypt extension or the Libsodium extension (both are available in PECL).

Bcrypt和scrypt是当前的最佳实践。Scrypt会比bcrypt更好,但是它还没有被Linux/Unix或webserver采用为标准,而且还没有对它的算法发表过深入的评论。但是,这个算法的未来还是很有前途的。如果您正在使用Ruby,有一个scrypt gem可以帮助您解决问题和节点。js现在有了自己的scrypt包。您可以通过Scrypt扩展或lib钠扩展来使用PHP的Scrypt(两者都可以在PECL中使用)。

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

如果您想了解如何使用bcrypt,或者找到一个好的包装器,或者使用PHPASS之类的东西来实现更多的遗留功能,我强烈建议您阅读crypt函数的文档。我建议至少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. PHPASS supports 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 certain that 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 PDKBF2 with the maximum number of rounds that 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 library that 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 layer for 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 crack a 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.

最后:我不是一个密码学家。无论我说什么都是我的观点,但我碰巧认为这是基于良好的常识……和大量的阅读。请记住,尽可能的偏执狂,让事情尽可能地难以侵入,然后,如果你还在担心,联系一个白帽黑客或密码学家,看看他们对你的代码/系统有什么看法。

#2


113  

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.
  • PHP 5.5或更高:password_hash()是良好的质量和PHP内核的一部分。
  • Older PHP versions: OpenWall's phpass library is much better than most custom code - used in WordPress, Drupal, etc.
  • 较老的PHP版本:OpenWall的phpass库要比大多数自定义代码好得多——在WordPress、Drupal等中使用。

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

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

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 FPGAs to 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 hours using 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.

例如,您可以在6个小时内使用安装在5个桌面pc中的25个gpu来破解所有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 still doesn't salt or stretch its passwords, even in Windows 10 - don't make the same mistake as Microsoft did!

还有许多针对Windows密码的彩虹表攻击,它们运行在普通cpu上,而且速度非常快。所有这一切都是因为Windows仍然不加盐,也不扩展它的密码,即使是在Windows 10中,也不要重蹈微软的覆辙!

See also:

参见:

  • excellent answer with more about why password_hash() or phpass are the best way to go.
  • 关于为什么password_hash()或phpass是最好的方法,这是一个非常好的答案。
  • good blog article giving recommmended 'work factors' (number of iterations) for main algorithms including bcrypt, scrypt and PBKDF2.
  • 优秀的博客文章为主要算法(包括bcrypt、scrypt和PBKDF2)提供了推荐的“工作因素”(迭代次数)。

#3


38  

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.

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

#4


28  

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- hash-salt -it- 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等)来处理的。这里有一个很好的解释。为了从散列中检索密码,您需要知道密码以及salt(网络名称)。“Wifi”已经预先计算了哈希表,它有1000个SSIDs和大约100万个密码。所有表的大小约为40 GB。你可以在他们的网站上看到,有人用了15个FGPA数组来生成这些表格。假设受害者使用SSID为“a387csf3”和密码为“123456”,会不会被这些表破解?不!. .它不能。即使密码很弱,表中也没有用于SSID a387csf3的哈希表。这就是随机盐的好处。它将阻止那些在预先计算过的桌子上茁壮成长的饼干。它能阻止一个坚定的黑客吗?可能不会。但是使用随机盐确实提供了额外的防御层。当我们讨论这个话题时,让我们讨论一下在单独的系统中存储随机盐的额外好处。场景# 1:密码散列存储在系统X和y盐用于哈希值存储在系统这些盐值可推测的或已知的(如用户名)场景# 2:密码散列存储在系统X和y盐用于哈希值存储在系统这些盐值是随机的。在系统X被破坏的情况下,你可以猜到,在一个单独的系统上使用随机的盐是一个巨大的优势(场景#2)。攻击者需要猜测附加值才能破解散列。如果使用一个32位的盐,2 ^ 32 = 4294967296(约42亿)迭代将可以要求每个密码猜。

#5


28  

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_DEFAULT instructs 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()中。

#6


24  

I just want to point out that PHP 5.5 includes a password hashing API that 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 require to use), for those using PHP 5.3.7 and later and want to use this right now.

我想指出的是,PHP 5.5包含了一个密码散列API,它提供了一个围绕crypt()的包装器。这个API显著地简化了哈希、验证和重新哈希密码散列的任务。作者还发布了一个兼容包(以单个密码的形式)。对于那些使用php 5.3.7和以后想要使用这个功能的php文件,您只需使用它即可。

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_get_info() -返回关于给定散列的信息。
  • password_hash() - creates a password hash
  • 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_needs_rehash()—检查给定的散列是否与给定的选项相匹配。检查散列是否符合当前的技术/成本计划,以便在必要时进行重新处理。
  • password_verify() - verifies that a password matches a hash
  • 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“可能会在更新的、更强的哈希算法得到支持时发生变化”。在登录时使用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的回答中被简单提到过。我将在这里留下这个答案,因为我认为它提供了更多关于它如何工作的信息,以及它为那些不知道安全性的人提供的便利。

#7


16  

I'm using Phpass which 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 bcrypt and falls back to other encryptions down to MD5 to provide backward compatibility to frameworks like Wordpress.

默认情况下,它使用了在Phpass中实现的最强大的可用加密,而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);

#8


12  

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.

对于世界上所有的好东西,不要使用用户名管理、根或类似的东西。而且,如果您在基于unix的系统上,没有使根帐户登录可访问,它应该始终是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和应用程序在不同的服务器上。这并不总是可能的,因为成本,但是它确实允许一些安全,因为攻击者必须经过两个步骤才能完全进入系统。

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.

遵循和所有好的密码一样的指导方针。也不要在同一系统上的任何服务器或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.

单向散列! ! ! ! ! ! !不要以一种可以颠倒的方式来哈希密码,哈希应该是一种方法,也就是说,你不会将它们反向,并将它们与密码进行比较,而是用同样的方法来哈希输入密码,并比较两个哈希。这意味着,即使攻击者访问了数据库,他也不知道实际的密码是什么,只是其结果哈希。这意味着在最糟糕的情况下,您的用户会得到更多的安全性。

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将随机字符串添加到密码中,这样类似的密码在DB中不会出现相同的情况。然而,如果盐不是每个用户都独有的(比如:你使用硬编码的盐),那么你的盐就变得一文不值了。因为一旦一个攻击者找到了一个密码,他就有了所有的盐。

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时,确保它是唯一的,它是salting,然后在您的DB中存储完成的散列和盐。这样做的目的是使攻击者在获得访问权限之前,必须分别破解每个盐和散列。这对攻击者来说意味着更多的工作和时间。

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中的所有安全性都是毫无价值的。总是安全地传输数据,这是通过SSL来完成的,但即使SSL也不是完美无缺的(OpenSSL的心脏出血缺陷就是一个例子)。

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.

最后,不管你采取什么安全措施,都是百分之百的安全,更先进的技术保护成为更先进的攻击。但是遵循这些步骤将使您的站点更加安全,并且对攻击者来说更不可取。

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

这里有一个PHP类,可以轻松地为密码创建一个散列和盐。

http://git.io/mSJqpw

http://git.io/mSJqpw

#9


11  

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字节应该没问题,我想。

#10


7  

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.

最后,从数学上讲,双哈希没有任何好处。然而,在实践中,它对于防止基于彩虹表的攻击非常有用。换句话说,它并不比用盐哈希更有用,因为它在应用程序或服务器上占用的处理器时间要少得多。

#11


7  

I found perfect topic on this matter here: https://crackstation.net/hashing-security.htm, I wanted you to get benefit from it, here is source code also that provided prevention against time-based attack also.

我在这里找到了一个完美的主题:https://crackstation.net/hashing-security.htm,我希望您从中获益,这里是源代码,也提供了预防基于时间的攻击的方法。

<?php
/*
 * Password hashing with PBKDF2.
 * Author: havoc AT defuse.ca
 * www: https://defuse.ca/php-pbkdf2.htm
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $good_hash)
{
    $params = explode(":", $good_hash);
    if(count($params) < HASH_SECTIONS)
       return false; 
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>

#12


5  

I usually use SHA1 and salt with the user ID (or some other user-specific piece of information), and sometimes I additionally use a constant salt (so I have 2 parts to the salt).

我通常使用SHA1和盐和用户ID(或者其他一些特定于用户的信息),有时候我还会使用一个常量盐(所以我有两个部分的盐)。

SHA1 is now also considered somewhat compromised, but to a far lesser degree than MD5. By using a salt (any salt), you're preventing the use of a generic rainbow table to attack your hashes (some people have even had success using Google as a sort of rainbow table by searching for the hash). An attacker could conceivably generate a rainbow table using your salt, so that's why you should include a user-specific salt. That way, they will have to generate a rainbow table for each and every record in your system, not just one for your entire system! With that type of salting, even MD5 is decently secure.

SHA1现在也被认为有些妥协,但程度远低于MD5。通过使用盐(任何盐),您就可以防止使用通用的彩虹表来攻击您的散列(有些人甚至使用谷歌作为一种彩虹表来搜索哈希表)。攻击者可以使用您的盐生成一个彩虹表,因此您应该包含一个特定于用户的盐。这样,他们就必须为系统中的每个记录生成一个彩虹表,而不仅仅是整个系统中的一个。有了这种盐渍,即使MD5也很安全。

#13


4  

SHA1 and a salt should suffice (depending, naturally, on whether you are coding something for Fort Knox or a login system for your shopping list) for the foreseeable future. If SHA1 isn't good enough for you, use SHA256.

在可预见的将来,SHA1和盐应该足够了(当然,这要取决于你是否在为诺克斯堡(Fort Knox)编码,或者为你的购物清单编写一个登录系统)。如果SHA1不够好,可以使用SHA256。

The idea of a salt is to throw the hashing results off balance, so to say. It is known, for example, that the MD5-hash of an empty string is d41d8cd98f00b204e9800998ecf8427e. So, if someone with good enough a memory would see that hash and know that it's the hash of an empty string. But if the string is salted (say, with the string "MY_PERSONAL_SALT"), the hash for the 'empty string' (i.e. "MY_PERSONAL_SALT") becomes aeac2612626724592271634fb14d3ea6, hence non-obvious to backtrace. What I'm trying to say, that it's better to use any salt, than not to. Therefore, it's not too much of an importance to know which salt to use.

所以说,盐的概念就是把散列的结果打乱。例如,已知空字符串的MD5-hash是d41d8cd98f00b204e9800998ecf8427e。因此,如果有足够好的内存的人会看到哈希,并且知道它是空字符串的散列。但是,如果字符串是salt(比如字符串“MY_PERSONAL_SALT”),则为“空字符串”的哈希值(即“空字符串”)。“MY_PERSONAL_SALT”)变成了aeac2612626724592271634fb14d3ea6,因此不明显可以回溯。我想说的是,最好是使用盐,而不是不加盐。因此,知道使用哪种盐并不重要。

There are actually websites that do just this - you can feed it a (md5) hash, and it spits out a known plaintext that generates that particular hash. If you would get access to a database that stores plain md5-hashes, it would be trivial for you to enter the hash for the admin to such a service, and log in. But, if the passwords were salted, such a service would become ineffective.

实际上有一些网站是这样做的——你可以给它提供一个(md5)散列,它会吐出一个已知的明文,生成那个特定的散列。如果您能够访问存储普通md5-哈希的数据库,那么您就可以为admin输入该服务的散列,然后登录。但是,如果密码被加了盐,这样的服务将变得无效。

Also, double-hashing is generally regarded as bad method, because it diminishes the result space. All popular hashes are fixed-length. Thus, you can have only a finite values of this fixed length, and the results become less varied. This could be regarded as another form of salting, but I wouldn't recommend it.

此外,双哈希通常被认为是不好的方法,因为它减少了结果空间。所有流行的散列都是固定长度的。因此,你只能有一个有限的值,这个固定长度,结果变得不那么多样化。这可以看作是另一种形式的盐腌,但我不会推荐它。

#14


-5  

ok in the fitsy we need salt salt must be unique so let generate it

在fitsy上,我们需要盐必须是独特的,所以让它产生。

   /**
     * Generating string
     * @param $size
     * @return string
     */
    function Uniwur_string($size){
        $text = md5(uniqid(rand(), TRUE));
        RETURN substr($text, 0, $size);
    }

also we need the hash I`m using sha512 it is the best and it is in php

我们还需要使用sha512的散列,它是最好的,它在php中。

   /**
     * Hashing string
     * @param $string
     * @return string
     */
    function hash($string){
        return hash('sha512', $string);
    }

so now we can use this functions to generate safe password

现在我们可以使用这个函数来生成安全的密码。

// generating unique password
$password = Uniwur_string(20); // or you can add manual password
// generating 32 character salt
$salt = Uniwur_string(32);
// now we can manipulate this informations

// hashin salt for safe
$hash_salt = hash($salt);
// hashing password
$hash_psw = hash($password.$hash_salt);

now we need to save in database our $hash_psw variable value and $salt variable

现在我们需要在数据库中保存$hash_psw变量值和$salt变量。

and for authorize we will use same steps...

对于授权我们将使用相同的步骤…

it is the best way to safe our clients passwords...

这是保护客户密码的最好方法……

P.s. for last 2 steps you can use your own algorithm... but be sure that you can generate this hashed password in the future when you need to authorize user...

最后两个步骤你可以使用你自己的算法…但是,当您需要授权用户时,请确保您可以在将来生成这个散列密码。

#1


871  

DISCLAIMER: This answer was written in 2008.

免责声明:这个答案写于2008年。

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

从那以后,PHP给我们提供了password_hash和password_verify,并且由于它们的介绍,它们是推荐的密码散列和检查方法。

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

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

TL;DR

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.
  • 不要限制密码的长度。如果你的用户想要一个具有supercalifragilisticexpialidocious的句子,不要阻止他们使用它。
  • 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 SHA1 or MD5 or even SHA256! Modern crackers can exceed 60 and 180 billion hashes/second (respectively).
  • 不要使用SHA1或MD5甚至SHA256来哈希密码!现代的饼干可以超过60和1800亿哈希/秒(分别)。
  • Don't mix bcrypt and with the raw output of hash(), either use hex output or base64_encode it. (This applies to any input that may have a rogue \0 in it, which can seriously weaken security.)
  • 不要混合bcrypt和散列()的原始输出,要么使用十六进制输出,要么使用base64_encode。(这适用于任何可能有流氓\0的输入,这将严重削弱安全性。)

Dos

  • Use scrypt when you can; bcrypt if you cannot.
  • 当你可以的时候使用scrypt;如果你不能bcrypt。
  • Use PBKDF2 if you cannot use either bcrypt or scrypt, with SHA2 hashes.
  • 如果您不能使用bcrypt或scrypt,使用SHA2散列,请使用PBKDF2。
  • 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.)
  • 实现一个合理的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 least lock the system down, if not change every password in the database.

你想要一个好的、健壮的用户帐户哈希的另一个原因是给你足够的时间来修改系统中的所有密码。如果您的数据库被破坏,您将需要足够的时间来至少锁定系统,如果不改变数据库中的每个密码。

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

杰里迈亚·格鲁斯曼(Jeremiah Grossman)是Whitehat 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 good password 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 approximated easily. 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 StackExchange site. A good Google search will also turn up a lot of results.

在加密交换站点上有一个关于密码熵的更长的讨论。一个好的谷歌搜索也会有很多的结果。

In the comments I talked with @popnoodles, who pointed out that enforcing a 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长度的密码策略(如字母、数字、符号等),可以通过使密码方案更可预测来减少熵。我同意。随机,尽可能的随机,总是最安全,但最不容易记住的解决方案。

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.

就我所知,让世界上最好的密码是第22条。它不是令人难忘的,太容易预测,太短,太多的unicode字符(很难在Windows/移动设备上打字),太长,等等。我们的目的没有密码,所以我们必须保护他们,就像他们在诺克斯堡一样。

Best practices

Bcrypt and scrypt are the current best practices. Scrypt will 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 gem that will help you out, and Node.js now has its own scrypt package. You can use Scrypt in PHP either via the Scrypt extension or the Libsodium extension (both are available in PECL).

Bcrypt和scrypt是当前的最佳实践。Scrypt会比bcrypt更好,但是它还没有被Linux/Unix或webserver采用为标准,而且还没有对它的算法发表过深入的评论。但是,这个算法的未来还是很有前途的。如果您正在使用Ruby,有一个scrypt gem可以帮助您解决问题和节点。js现在有了自己的scrypt包。您可以通过Scrypt扩展或lib钠扩展来使用PHP的Scrypt(两者都可以在PECL中使用)。

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

如果您想了解如何使用bcrypt,或者找到一个好的包装器,或者使用PHPASS之类的东西来实现更多的遗留功能,我强烈建议您阅读crypt函数的文档。我建议至少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. PHPASS supports 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 certain that 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 PDKBF2 with the maximum number of rounds that 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 library that 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 layer for 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 crack a 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.

最后:我不是一个密码学家。无论我说什么都是我的观点,但我碰巧认为这是基于良好的常识……和大量的阅读。请记住,尽可能的偏执狂,让事情尽可能地难以侵入,然后,如果你还在担心,联系一个白帽黑客或密码学家,看看他们对你的代码/系统有什么看法。

#2


113  

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.
  • PHP 5.5或更高:password_hash()是良好的质量和PHP内核的一部分。
  • Older PHP versions: OpenWall's phpass library is much better than most custom code - used in WordPress, Drupal, etc.
  • 较老的PHP版本:OpenWall的phpass库要比大多数自定义代码好得多——在WordPress、Drupal等中使用。

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

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

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 FPGAs to 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 hours using 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.

例如,您可以在6个小时内使用安装在5个桌面pc中的25个gpu来破解所有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 still doesn't salt or stretch its passwords, even in Windows 10 - don't make the same mistake as Microsoft did!

还有许多针对Windows密码的彩虹表攻击,它们运行在普通cpu上,而且速度非常快。所有这一切都是因为Windows仍然不加盐,也不扩展它的密码,即使是在Windows 10中,也不要重蹈微软的覆辙!

See also:

参见:

  • excellent answer with more about why password_hash() or phpass are the best way to go.
  • 关于为什么password_hash()或phpass是最好的方法,这是一个非常好的答案。
  • good blog article giving recommmended 'work factors' (number of iterations) for main algorithms including bcrypt, scrypt and PBKDF2.
  • 优秀的博客文章为主要算法(包括bcrypt、scrypt和PBKDF2)提供了推荐的“工作因素”(迭代次数)。

#3


38  

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.

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

#4


28  

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- hash-salt -it- 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等)来处理的。这里有一个很好的解释。为了从散列中检索密码,您需要知道密码以及salt(网络名称)。“Wifi”已经预先计算了哈希表,它有1000个SSIDs和大约100万个密码。所有表的大小约为40 GB。你可以在他们的网站上看到,有人用了15个FGPA数组来生成这些表格。假设受害者使用SSID为“a387csf3”和密码为“123456”,会不会被这些表破解?不!. .它不能。即使密码很弱,表中也没有用于SSID a387csf3的哈希表。这就是随机盐的好处。它将阻止那些在预先计算过的桌子上茁壮成长的饼干。它能阻止一个坚定的黑客吗?可能不会。但是使用随机盐确实提供了额外的防御层。当我们讨论这个话题时,让我们讨论一下在单独的系统中存储随机盐的额外好处。场景# 1:密码散列存储在系统X和y盐用于哈希值存储在系统这些盐值可推测的或已知的(如用户名)场景# 2:密码散列存储在系统X和y盐用于哈希值存储在系统这些盐值是随机的。在系统X被破坏的情况下,你可以猜到,在一个单独的系统上使用随机的盐是一个巨大的优势(场景#2)。攻击者需要猜测附加值才能破解散列。如果使用一个32位的盐,2 ^ 32 = 4294967296(约42亿)迭代将可以要求每个密码猜。

#5


28  

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_DEFAULT instructs 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()中。

#6


24  

I just want to point out that PHP 5.5 includes a password hashing API that 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 require to use), for those using PHP 5.3.7 and later and want to use this right now.

我想指出的是,PHP 5.5包含了一个密码散列API,它提供了一个围绕crypt()的包装器。这个API显著地简化了哈希、验证和重新哈希密码散列的任务。作者还发布了一个兼容包(以单个密码的形式)。对于那些使用php 5.3.7和以后想要使用这个功能的php文件,您只需使用它即可。

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_get_info() -返回关于给定散列的信息。
  • password_hash() - creates a password hash
  • 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_needs_rehash()—检查给定的散列是否与给定的选项相匹配。检查散列是否符合当前的技术/成本计划,以便在必要时进行重新处理。
  • password_verify() - verifies that a password matches a hash
  • 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“可能会在更新的、更强的哈希算法得到支持时发生变化”。在登录时使用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的回答中被简单提到过。我将在这里留下这个答案,因为我认为它提供了更多关于它如何工作的信息,以及它为那些不知道安全性的人提供的便利。

#7


16  

I'm using Phpass which 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 bcrypt and falls back to other encryptions down to MD5 to provide backward compatibility to frameworks like Wordpress.

默认情况下,它使用了在Phpass中实现的最强大的可用加密,而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);

#8


12  

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.

对于世界上所有的好东西,不要使用用户名管理、根或类似的东西。而且,如果您在基于unix的系统上,没有使根帐户登录可访问,它应该始终是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和应用程序在不同的服务器上。这并不总是可能的,因为成本,但是它确实允许一些安全,因为攻击者必须经过两个步骤才能完全进入系统。

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.

遵循和所有好的密码一样的指导方针。也不要在同一系统上的任何服务器或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.

单向散列! ! ! ! ! ! !不要以一种可以颠倒的方式来哈希密码,哈希应该是一种方法,也就是说,你不会将它们反向,并将它们与密码进行比较,而是用同样的方法来哈希输入密码,并比较两个哈希。这意味着,即使攻击者访问了数据库,他也不知道实际的密码是什么,只是其结果哈希。这意味着在最糟糕的情况下,您的用户会得到更多的安全性。

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将随机字符串添加到密码中,这样类似的密码在DB中不会出现相同的情况。然而,如果盐不是每个用户都独有的(比如:你使用硬编码的盐),那么你的盐就变得一文不值了。因为一旦一个攻击者找到了一个密码,他就有了所有的盐。

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时,确保它是唯一的,它是salting,然后在您的DB中存储完成的散列和盐。这样做的目的是使攻击者在获得访问权限之前,必须分别破解每个盐和散列。这对攻击者来说意味着更多的工作和时间。

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中的所有安全性都是毫无价值的。总是安全地传输数据,这是通过SSL来完成的,但即使SSL也不是完美无缺的(OpenSSL的心脏出血缺陷就是一个例子)。

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.

最后,不管你采取什么安全措施,都是百分之百的安全,更先进的技术保护成为更先进的攻击。但是遵循这些步骤将使您的站点更加安全,并且对攻击者来说更不可取。

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

这里有一个PHP类,可以轻松地为密码创建一个散列和盐。

http://git.io/mSJqpw

http://git.io/mSJqpw

#9


11  

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字节应该没问题,我想。

#10


7  

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.

最后,从数学上讲,双哈希没有任何好处。然而,在实践中,它对于防止基于彩虹表的攻击非常有用。换句话说,它并不比用盐哈希更有用,因为它在应用程序或服务器上占用的处理器时间要少得多。

#11


7  

I found perfect topic on this matter here: https://crackstation.net/hashing-security.htm, I wanted you to get benefit from it, here is source code also that provided prevention against time-based attack also.

我在这里找到了一个完美的主题:https://crackstation.net/hashing-security.htm,我希望您从中获益,这里是源代码,也提供了预防基于时间的攻击的方法。

<?php
/*
 * Password hashing with PBKDF2.
 * Author: havoc AT defuse.ca
 * www: https://defuse.ca/php-pbkdf2.htm
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $good_hash)
{
    $params = explode(":", $good_hash);
    if(count($params) < HASH_SECTIONS)
       return false; 
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>

#12


5  

I usually use SHA1 and salt with the user ID (or some other user-specific piece of information), and sometimes I additionally use a constant salt (so I have 2 parts to the salt).

我通常使用SHA1和盐和用户ID(或者其他一些特定于用户的信息),有时候我还会使用一个常量盐(所以我有两个部分的盐)。

SHA1 is now also considered somewhat compromised, but to a far lesser degree than MD5. By using a salt (any salt), you're preventing the use of a generic rainbow table to attack your hashes (some people have even had success using Google as a sort of rainbow table by searching for the hash). An attacker could conceivably generate a rainbow table using your salt, so that's why you should include a user-specific salt. That way, they will have to generate a rainbow table for each and every record in your system, not just one for your entire system! With that type of salting, even MD5 is decently secure.

SHA1现在也被认为有些妥协,但程度远低于MD5。通过使用盐(任何盐),您就可以防止使用通用的彩虹表来攻击您的散列(有些人甚至使用谷歌作为一种彩虹表来搜索哈希表)。攻击者可以使用您的盐生成一个彩虹表,因此您应该包含一个特定于用户的盐。这样,他们就必须为系统中的每个记录生成一个彩虹表,而不仅仅是整个系统中的一个。有了这种盐渍,即使MD5也很安全。

#13


4  

SHA1 and a salt should suffice (depending, naturally, on whether you are coding something for Fort Knox or a login system for your shopping list) for the foreseeable future. If SHA1 isn't good enough for you, use SHA256.

在可预见的将来,SHA1和盐应该足够了(当然,这要取决于你是否在为诺克斯堡(Fort Knox)编码,或者为你的购物清单编写一个登录系统)。如果SHA1不够好,可以使用SHA256。

The idea of a salt is to throw the hashing results off balance, so to say. It is known, for example, that the MD5-hash of an empty string is d41d8cd98f00b204e9800998ecf8427e. So, if someone with good enough a memory would see that hash and know that it's the hash of an empty string. But if the string is salted (say, with the string "MY_PERSONAL_SALT"), the hash for the 'empty string' (i.e. "MY_PERSONAL_SALT") becomes aeac2612626724592271634fb14d3ea6, hence non-obvious to backtrace. What I'm trying to say, that it's better to use any salt, than not to. Therefore, it's not too much of an importance to know which salt to use.

所以说,盐的概念就是把散列的结果打乱。例如,已知空字符串的MD5-hash是d41d8cd98f00b204e9800998ecf8427e。因此,如果有足够好的内存的人会看到哈希,并且知道它是空字符串的散列。但是,如果字符串是salt(比如字符串“MY_PERSONAL_SALT”),则为“空字符串”的哈希值(即“空字符串”)。“MY_PERSONAL_SALT”)变成了aeac2612626724592271634fb14d3ea6,因此不明显可以回溯。我想说的是,最好是使用盐,而不是不加盐。因此,知道使用哪种盐并不重要。

There are actually websites that do just this - you can feed it a (md5) hash, and it spits out a known plaintext that generates that particular hash. If you would get access to a database that stores plain md5-hashes, it would be trivial for you to enter the hash for the admin to such a service, and log in. But, if the passwords were salted, such a service would become ineffective.

实际上有一些网站是这样做的——你可以给它提供一个(md5)散列,它会吐出一个已知的明文,生成那个特定的散列。如果您能够访问存储普通md5-哈希的数据库,那么您就可以为admin输入该服务的散列,然后登录。但是,如果密码被加了盐,这样的服务将变得无效。

Also, double-hashing is generally regarded as bad method, because it diminishes the result space. All popular hashes are fixed-length. Thus, you can have only a finite values of this fixed length, and the results become less varied. This could be regarded as another form of salting, but I wouldn't recommend it.

此外,双哈希通常被认为是不好的方法,因为它减少了结果空间。所有流行的散列都是固定长度的。因此,你只能有一个有限的值,这个固定长度,结果变得不那么多样化。这可以看作是另一种形式的盐腌,但我不会推荐它。

#14


-5  

ok in the fitsy we need salt salt must be unique so let generate it

在fitsy上,我们需要盐必须是独特的,所以让它产生。

   /**
     * Generating string
     * @param $size
     * @return string
     */
    function Uniwur_string($size){
        $text = md5(uniqid(rand(), TRUE));
        RETURN substr($text, 0, $size);
    }

also we need the hash I`m using sha512 it is the best and it is in php

我们还需要使用sha512的散列,它是最好的,它在php中。

   /**
     * Hashing string
     * @param $string
     * @return string
     */
    function hash($string){
        return hash('sha512', $string);
    }

so now we can use this functions to generate safe password

现在我们可以使用这个函数来生成安全的密码。

// generating unique password
$password = Uniwur_string(20); // or you can add manual password
// generating 32 character salt
$salt = Uniwur_string(32);
// now we can manipulate this informations

// hashin salt for safe
$hash_salt = hash($salt);
// hashing password
$hash_psw = hash($password.$hash_salt);

now we need to save in database our $hash_psw variable value and $salt variable

现在我们需要在数据库中保存$hash_psw变量值和$salt变量。

and for authorize we will use same steps...

对于授权我们将使用相同的步骤…

it is the best way to safe our clients passwords...

这是保护客户密码的最好方法……

P.s. for last 2 steps you can use your own algorithm... but be sure that you can generate this hashed password in the future when you need to authorize user...

最后两个步骤你可以使用你自己的算法…但是,当您需要授权用户时,请确保您可以在将来生成这个散列密码。