PHP 最强的一种加密/散列方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16133884/
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
PHP Strongest one way encryption/hashing method
提问by user1763295
I have a site on which people can sign up and I need to encrypt their password. I've researched it but I can't find any methods that won't be beat by the power of a modern GPU.
我有一个人们可以注册的网站,我需要加密他们的密码。我已经研究过它,但我找不到任何方法不会被现代 GPU 的力量打败。
So I have come to the good people of StackOverflow to ask, what is the strongest method of encryption possible, I have done my best to stop people getting their hands on the database but I want to be as sure as possible that their data would be fine if the database was somehow stolen.
所以我来到 StackOverflow 的好人那里问,什么是最强大的加密方法,我已尽力阻止人们接触数据库,但我想尽可能确定他们的数据会是如果数据库以某种方式被盗,那很好。
Other things I wonder are, would be it be safer to just randomize the characters in the password somehow but in a way where you would be able to randomize them again for login?
我想知道的其他事情是,以某种方式随机化密码中的字符是否更安全,但您可以再次随机化它们以进行登录?
Edit:I have used the implementation of bcrypt by Andrew Moore (How do you use bcrypt for hashing passwords in PHP?) and come up with this:
编辑:我使用了 Andrew Moore 的 bcrypt 实现(你如何使用 bcrypt 在 PHP 中散列密码?)并提出这个:
public static function Encrypt($Str,$Salt)
{
$bcrypt = new \bcrypt();
return $bcrypt->hash(SERVER_SALT . md5($Str) . $Salt);
}
if anyone sees anything wrong with it or any weaknesses please tell me.
如果有人发现它有任何问题或任何弱点,请告诉我。
回答by LSerni
For passwords, you can't beat bcrypt
. Here is a link on SO: How do you use bcrypt for hashing passwords in PHP?. Its main advantage: it is inherently slow(as well as secure). While a normal user will use it once, and will not appreciate the difference between one tenth and one millionth of a second, a cracker will discover that instead of finding the password in four days, he now needs twelve centuries(except that forcing Blowfish even at one million attempts per second does not require four days: barring implementation errors and as yet unforeseen crypto breakthroughs, the heat death of the Universe will still come first).
对于密码,您无法击败bcrypt
. 这是关于 SO 的链接:How do you use bcrypt for hashing password in PHP? . 它的主要优点是:它本质上很慢(也很安全)。虽然普通用户会使用一次,并且不会意识到十分之一秒和百万分之一秒之间的差异,但破解者会发现,不是在四天内找到密码,他现在需要十二个世纪(除了强制 Blowfish 甚至每秒 100 万次尝试不需要四天:除非实施错误和尚未预见的加密技术突破,宇宙的热死仍将首先出现)。
For data, I'd rely on the database engine itself; MySQL supports AES, and that's quite good. Yes, with enough GPUs one could beat it, and since to crack the code within the century one would need some four billions of them, he'd probably get a good quantity discount too. But I wouldn't worry.
对于数据,我会依赖数据库引擎本身;MySQL 支持 AES,这很好。是的,拥有足够多的 GPU 就可以击败它,而且由于要在本世纪内破解密码,一个人需要大约 40 亿个 GPU,他也可能会获得很好的数量折扣。但我不会担心。
As for the "randomization" of the password, it would serve no purpose. If someone were to resort to bruteforcing your password, he would have the same probability of finding the randomized sequence or the non-randomized one. It might be of some help if one were to use a dictionary attack(i.e., not trying everything from AAAAA to ZZZZZ, but trying ABRAHAM, ACCORDION, ... up to, say, ZYGOTE). But even then, an attack against the server would change nothing (the server receives the non-randomized password!), and an attack against the stored hash would be better nullified by salting, which bcrypt
automatically does: i.e., storing the hash of $76X.J:MICKEYMOUSE instead of the hash of MICKEYMOUSE, together with the needed overhead to handle the extra '$76X.J:' on input. And while the randomization would have to be fixed, the '$76X.J:' sequence is completely different on every write-- good luck figuring thatout!
至于密码的“随机化”,则无济于事。如果有人使用暴力破解您的密码,他将有相同的概率找到随机序列或非随机序列。如果要使用字典攻击(即,不是尝试从 AAAAA 到 ZZZZZ 的所有内容,而是尝试 ABRAHAM、ACCORDION ……直到,例如 ZYGOTE),这可能会有所帮助。但即便如此,对服务器的攻击也不会改变任何东西(服务器收到非随机密码!),并且对存储的哈希的攻击最好通过salting无效,这bcrypt
自动执行:即,存储 $76XJ:MICKEYMOUSE 的散列而不是 MICKEYMOUSE 的散列,以及处理输入中额外的 '$76XJ:' 所需的开销。而且,尽管随机必须是固定的,在“$ 76XJ:”序列是在每次写入完全不同的-好运气搞清楚那个了!
Salting
盐
If you implement salting yourself (as shown above, with bcrypt
you don't need to), you can do it by generating a unique random sequence and storing it either in the password field or in a second field. For example
如果您自己实现加盐(如上所示,bcrypt
您不需要),您可以通过生成唯一的随机序列并将其存储在密码字段或第二个字段中来实现。例如
user password
alice d9c06f88:6c14d6d313d7cbcb132b5c63650682c4
Then, upon receipt of the password by Alice ("mickeymouse"), you would look in the database to see whether a user called alice
exists. If it does, recover the salt (here d9c06f88
) and the hash. If it does not, set a "BAD" flag and fetch a fixed salt and hash (e.g. 12345678:0000000...
).
然后,在收到 Alice ("mickeymouse") 的密码后,您将在数据库中查看是否alice
存在一个名为的用户。如果是,请恢复盐(此处d9c06f88
)和散列。如果没有,请设置“BAD”标志并获取固定的盐和哈希(例如12345678:0000000...
)。
In MySQL this can be done using UNION:
在 MySQL 中,这可以使用 UNION 来完成:
SELECT password_fields FROM users WHERE user=? AND hash=?
UNION SELECT
'12345789' as salt,
'ffffffffffffffffffffffff' as hash,
'fake' as user
LIMIT 1;
will retrieve either the correct data or an incorrect set in roughly the same time (this prevents someone to guess which names exist and which do not by timing the time needed to answer "wrong user or password").
将在大致相同的时间内检索正确的数据或不正确的数据集(这可以防止有人通过计时回答“错误的用户或密码”所需的时间来猜测哪些名称存在,哪些不存在)。
Then concatenate the salt and the password, and generate the hash of d9c06f88:mickeymouse
. If it does not match, orthe "BAD" flag is set, reject the password (instead of using a 'bad' flag, you can repeat the test for user match that MySQL already did: you can initialize the user name by replacing the last character with an invalid one, to ensure it will never match a real user).
然后连接盐和密码,并生成d9c06f88:mickeymouse
. 如果不匹配,或者设置了“BAD”标志,则拒绝密码(而不是使用“坏”标志,您可以重复 MySQL 已经进行的用户匹配测试:您可以通过替换最后一个字符无效,以确保它永远不会与真实用户匹配)。
Security through noninformation
通过非信息确保安全
The added twist of selecting a fixed string is useful because you want the three cases, "user does not exist", "user exists but password is incorrect" and "user exists and password is correct" to be as similar (same complexity, same expense in calculations) as possible.
选择固定字符串的附加扭曲很有用,因为您希望“用户不存在”、“用户存在但密码不正确”和“用户存在且密码正确”三种情况相似(相同的复杂性,相同的计算中的费用)尽可能。
This way, an attacker will be less likely to tell what happened: was the user incorrect? Was the password wrong? And so on. If the times were different enough (say two queries for valid users, one for invalid users), an attacker with care, time and a good stopwatch could determine statistically whether a given username is present or not.
这样,攻击者就不太可能知道发生了什么:用户是否不正确?密码错了吗?等等。如果时间足够不同(比如对有效用户进行两次查询,对无效用户进行一次查询),小心、时间和好的秒表的攻击者可以从统计上确定给定的用户名是否存在。
In the case of the user, even the name comparison will be
在用户的情况下,甚至名称比较将是
johnsmith against johnsmith if johnsmith exists
johnsmith against johnsmit? if johnsmith does not exist
so telling what happened from the other end of a HTTP(s) connection won't be easy.
所以从 HTTP(s) 连接的另一端知道发生了什么并不容易。
For the same reason, you will notreturn two different errors for "Bad user" and "Bad password", but always a "Bad user or password"; maybe with the option, for the user, to receive an email to his/her registered email to remind him/her of the username. And of course, you want the system to notsend such an email before 24 hours have passed from sending a similar email to the same user, to prevent the system from being exploited to harass someone with spurious "recovery emails".
出于同样的原因,您不会返回“Bad user”和“Bad password”两个不同的错误,而是始终返回“Bad user or password”;对于用户来说,也许可以选择在他/她的注册电子邮件中收到一封电子邮件,以提醒他/她的用户名。当然,您希望系统在向同一用户发送类似电子邮件后 24 小时内不发送此类电子邮件,以防止系统被利用以虚假的“恢复电子邮件”骚扰某人。
If you dosend the email, you will wait until a preset time has expired (say, 3 seconds), and then inform users that ifthe username was present, thenthey should check their inbox (and the spam folder for good measure).
如果您确实发送了电子邮件,您将等到预设时间到期(例如 3 秒),然后通知用户如果用户名存在,那么他们应该检查他们的收件箱(以及垃圾邮件文件夹以进行良好的衡量)。
There would have been a time for such a password
会有这样一个密码的时间
A convenient way to improve server security against bruteforcing is to implement a delayon password authentication, and maybe (if you're really paranoid) a CAPTCHA-ed lockout after some X wrong attempts.
提高服务器安全性以防止暴力破解的一种便捷方法是对密码身份验证实施延迟,并且可能(如果您真的很偏执)在 X 次错误尝试后进行验证码锁定。
You don't want to CAPTCHA the first attempts because users take a dim view of CAPTCHAs.
您不希望第一次尝试验证码,因为用户对验证码的看法很模糊。
Delayed lockout is often implemented with sleep
(or equivalent), or using a "lock out until manually reset by the admin" strategy. Both methods are bad. The lockout feature can be used to create a denial of service attack, either by locking out an user, or creating lots of server threads stopped in "password authentication delay" state (they won't use CPU, but they will stilluse memory, sockets and other resources).
延迟锁定通常使用sleep
(或等效的)或使用“锁定直到管理员手动重置”策略来实现。这两种方法都不好。锁定功能可用于创建拒绝服务攻击,通过锁定用户,或创建大量在“密码验证延迟”状态下停止的服务器线程(它们不会使用 CPU,但仍会使用内存,套接字和其他资源)。
In some cases this can happen unwittingly. There seems to be some idiot who uses my bank, and every couple of months I get a SMS saying "WRONG PIN ENTERED IN YOUR HOME BANK SYSTEM". Then I have to log in from my phone, to reset the unsuccessful attempt counter; because if the idiot doesn't realize that he can't get in hisaccount because he entered myaccount number, for three times, it is MYaccount that gets locked and I have to go physically to the bank and beg them to reset my access. This, let me tell you, is a majorpain in the nether regions, and even knowing it's not their fault I stillresent my bank. You don't want to engender such feelings in your users.
在某些情况下,这可能会在不知不觉中发生。似乎有一些白痴在使用我的银行,每隔几个月我就会收到一条短信,内容是“在您的家庭银行系统中输入了错误的密码”。然后我必须从我的手机登录,重置不成功的尝试计数器;因为如果白痴没有意识到他因为输入了我的账号而无法进入他的账户,三遍,我的账户被锁定,我必须亲自去银行请求他们重置我的使用权。我告诉你,这是下界的一大痛点,即使知道这不是他们的错,我仍然怨恨我的银行。您不希望在您的用户中产生这种感觉。
And it is best to move the burden on the client:
最好将负担转移到客户端:
(very pseudo code)
login = FAIL
if in SECURITY LOCKOUT MODE for this account
if a session is open and contains a last-attempt time
if at least DELAY seconds have elapsed since last-attempt
check the password
if it is correct
login = OK
zero password counter, exit lockout mode.
# An "innocent" user enters with no lockout! Yay!
end
else
# "Early knocker". Either a bruteforcing robot
# or a too clever user in a hurry. But we can't
# tell them apart.
end
else
# No session. Either a sessionless bruteforcing robot
# or a unaware, innocent user. Again we can't tell them
# apart. So we bounce both.
# But a genuine user will assume he has mistyped the password,
# or anyway will read the warning page, and will login after ONE
# round of delay.
# Users with password saved in browser will just click
# "login" again and be logged in.
# A robot will find itself delayed and ALL ITS PASSWORDS IGNORED
# every single time. Even if it finds the right password... it will
# not work.
end
else
check the password
if it is correct
# Good user, first attempt, fast login.
login = OK
else
# Beginning to doubt this is a good user...
increase password counter
if it is > MAX_ATTEMPTS
enter SECURITY LOCKOUT MODE for this account
end
end
end
if login is not OK
generate a page with HTTP_REFRESH time of DELAY+1 seconds
and a session ID, saying "User or password unknown,
or you tried to login before HH:MM:SS (DELAY seconds).
The page might also contain a Javascript timer, just in
case. The delay is 1s more than necessary as a safety
margin.
end
回答by wwjdm
AES 256-bit encryption is very "strong" meaning it is one of the latest encryption standards that is hard to crack. I have read that if you take a brute force approach to crack it you would need all the computing power of the world.
AES 256 位加密非常“强大”,这意味着它是难以破解的最新加密标准之一。我读过,如果你采用蛮力方法来破解它,你将需要世界上所有的计算能力。
Check out:
查看:
How to do AES256 decryption in PHP?
And:
和:
http://en.wikipedia.org/wiki/Advanced_Encryption_Standardhttp://www.andrew-kirkpatrick.com/2013/01/aes-encryption-with-php-256-bit-using-iv/
http://en.wikipedia.org/wiki/Advanced_Encryption_Standard http://www.andrew-kirkpatrick.com/2013/01/aes-encryption-with-php-256-bit-using-iv/
Hope that helps.
希望有帮助。