你如何在 MySQL 中安全地存储用户的密码和盐?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7270526/
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-31 20:58:37  来源:igfitidea点击:

How do you securely store a user's password and salt in MySQL?

mysqlpasswordssha1

提问by

So, I found out on SO that you're supposed to hash the password together with a "salt". (The articles can be found hereand here.)

因此,我在 SO 上发现您应该将密码与“盐”一起散列。(这些文章可以在这里这里找到。)

Here's the code:

这是代码:

$password = 'fish';

/* should be "unique" for every user? */
$salt= 'ABC09';

$site_key = 'static_site_key';

hash_hmac('sha1', $password . $salt, $site_key);

And now I need to save both the $passwordand $saltin MySQL, like so:

现在我需要将$password和都保存$salt在 MySQL 中,如下所示:

+---------+--------+----------+-------+
| user_id |  name  | password |  salt |
+---------+--------+----------+-------+
|    1    | krysis |  fish**  | ABC09 |
+---------+--------+----------+-------+

** fishwill of course be hashed and not stored in plain text.

**fish当然会被散列而不是存储在纯文本中。

And I'm just wondering whether or not it actually makes sense to do it this way, because this way a hacker or whoever will also know the salt? So, if they crack the password and the see it's fishABC09they automatically will know the password is fish? Or might he "never" be able to crack the password because he doesn't know the secret_key, as it isn't stored in the database?

我只是想知道这样做是否真的有意义,因为这样黑客或任何人也会知道盐?那么,如果他们破解密码并且看到它,fishABC09他们会自动知道密码是fish?或者他可能“永远”无法破解密码,因为他不知道secret_key,因为它没有存储在数据库中?

I'm sorry if I'm not making any sense. I just always used sha1for passwords, and today I found these articles that talked about adding a salt.

如果我没有任何意义,我很抱歉。我只是一直使用sha1密码,今天我发现这些文章谈到了添加salt.

采纳答案by kto

There are good articles about storing passwords right. One of them for example: Storing Passwords - done right!

有关于正确存储密码的好文章。例如其中之一:存储密码 - 做对了!

You should use different salt for every user, but there's no need to store the salts separately. See similar discussion in another thread

您应该为每个用户使用不同的盐,但无需单独存储盐。请参阅另一个线程中的类似讨论

By the way, you probably shouldn't be using sha1 but e.g. sha256 or sha512 something stronger instead (at least to avoid bad publicity). There's a good answer regarding this: How insecure is a salted SHA1 compared to a salted SHA512

顺便说一句,您可能不应该使用 sha1 而是例如 sha256 或 sha512 一些更强大的东西(至少是为了避免不良宣传)。对此有一个很好的答案:与盐渍 SHA512 相比,盐渍 SHA1 的安全性如何

回答by you cad sir - take that

Rainbow / Dictionary Attacks, and salted passwords - a heretics approach

Rainbow / Dictionary Attacks, and salted passwords - 异端方法

Passwords should not be stored in your database. Do not roll your own authentication mechanism - you will almost certainly get it wrong. Use Kereberos for valuable stuff, and well I dont have a good suggestion otherwise.

密码不应存储在您的数据库中。不要推出自己的身份验证机制 - 你几乎肯定会弄错。将 Kereberos 用于有价值的东西,否则我没有好的建议。

However, this question has been bashing around in my skull for a while (see the edits) and I would like to put a heretical viewpoint over.

然而,这个问题已经在我的头骨中徘徊了一段时间(见编辑),我想提出一个异端观点。

Rainbow tables are so-called because of the way the lookup mechanism (chaining) is used - but they are just dictionary attacks. Millions of dictionary words and common passphrases are hashed up front and then used to compare stolen hashed passwords against.

之所以称为彩虹表,是因为使用了查找机制(链接)的方式 - 但它们只是字典攻击。数以百万计的字典单词和常用密码被预先散列,然后用于比较窃取的散列密码。

This worked well for the NT4 password process which used md5 hashes and no salt. But when a salt is added to a password before hashing, then the rainbow table is useless - instead of looking for the md5 hash of "mypass" it has to precompute "mypass-random-string_of-letters"

这对于使用 md5 散列且不使用盐的 NT4 密码过程非常有效。但是当在散列之前将盐添加到密码中时,彩虹表就没有用了 - 而不是寻找“mypass”的 md5 散列,它必须预先计算“mypass-random-string_of-letters”

Its impossible to guess what salt someone will use, so salting makes rainbow tables as a generic, use anywhere against any server solution dead in the water.

不可能猜测某人会使用什么盐,因此加盐使彩虹表成为通用的,可在任何地方使用,以对抗死在水中的任何服务器解决方案。

but ...

但 ...

Thats only one use case - certainly a big threat, certainly one to defend against. But salts have a problem. You must keep the salt around for when you want to authenticate the next time user logs in. They send their plaintext (over ssl!), you append the salt and hash, comapre to the hash stored in database. But if you dont keep the salt with the password, you cannot do that, and errr... no login

那只是一个用例——当然是一个很大的威胁,当然是一个需要防御的威胁。但是盐有问题。当您想在下次用户登录时进行身份验证时,您必须保留盐。他们发送他们的纯文本(通过 ssl!),您将盐和哈希附加到存储在数据库中的哈希中。但是,如果您不使用密码保留盐,则不能这样做,而且 errr... 没有登录

But we are not only defending against people passing around a table desgined to crack NT4 passwrods. We are supposed to protect our users individually.

但我们不仅要防止人们绕过旨在破解 NT4 密码的桌子。我们应该单独保护我们的用户。

A salt adds a two factor defence to the passwords - even with the hash an attacker will need the salt to have any chance of cracking it. But the standard advice just gives away that two factor defence. Probably its reasonable, but I am not convinced.

盐为密码增加了两个因素的防御——即使使用散列,攻击者也需要盐来破解它。但是标准的建议只是放弃了两个因素的防御。可能它是合理的,但我不相信。

There is a bit of maths behind this. The usual advice (given by RSA as well - ftp.rsa.com/pub/pkcs/pkcs-5v2/pkcs5v2-0.pdf) is build a 64 bit salt, store it with the hashed password. This way you can re-confirm the password by rehashing with the salt and reversing the hash is next to impossible.

这背后有一些数学问题。通常的建议(也由 RSA 给出 - ftp.rsa.com/pub/pkcs/pkcs-5v2/pkcs5v2-0.pdf)是构建一个 64 位盐,用散列密码存储它。通过这种方式,您可以通过使用盐重新散列来重新确认密码,并且几乎不可能反转散列。

NB - I could be wrong here ...

注意 - 我可能在这里错了......

The "next to impossible" bit comes from simple maths.

“几乎不可能”位来自简单的数学。

Lets assume an 8 digit password, with 62 possible characters (letters, upper lower and numbers)

让我们假设一个 8 位密码,有 62 个可能的字符(字母、上下和数字)

Thats 62^8 combinations, or a little over 200 million trillion.

那是 62^8 种组合,或略高于 2 亿万亿。

To brute force this, by computing the hash directly, (ie making my salt specific rainbow tables) I should see a collision after 62^8/2 and at lets say 100 hashes per second, it will take around 12 million days. Yes days.

为了强制执行此操作,通过直接计算散列(即制作我的盐特定彩虹表),我应该在 62^8/2 之后看到碰撞,假设每秒 100 个散列,大约需要 1200 万天。是天。

OK, so even storing the hash with the password makes the task of finding the hash quite infeasible.

好的,因此即使将哈希与密码一起存储,也使得查找哈希的任务变得非常不可行。

However there are some assumptions in the above. Firstly that the password is a random choice of the range 62^8. In practise most passwords a much much weaker - rainbow tables aren't really based on all 62^8 possibilities - they are built out of dictionaries, and real password tables found over the years.

然而,上面有一些假设。首先,密码是 62^8 范围内的随机选择。在实践中,大多数密码要弱得多——彩虹表并不是真正基于所有 62^8 种可能性——它们是由字典和多年来发现的真实密码表构建的。

So the search space, instead of being 62^8 is really smaller. How small ? The depends on the password "strength".

所以搜索空间,而不是 62^8 真的更小。多么小 ?这取决于密码“强度”。

There are about 250,000 - 750,000 words in english language (http://oxforddictionaries.com/page/93). Lets take 500,000 as a simple case. Then lets take variations that can be applied - add a digit, add a year, convert vowels to digits. That gives us say 3 possible new words per word, or 2 million possible passwords.

英语大约有 250,000 - 750,000 个单词 (http://oxforddictionaries.com/page/93)。让我们以 500,000 为例。然后让我们采取可以应用的变体 - 添加一个数字,添加一个年份,将元音转换为数字。这给了我们每个单词 3 个可能的新词,或 200 万个可能的密码。

Generating 2 million hashes at 100 / sec give 20,000 seconds or 5 hours - well within the range of any laptop.

以 100 个/秒的速度生成 200 万个哈希值需要 20,000 秒或 5 小时——这在任何笔记本电脑的范围内。

So if there is a specific user being targeted, (root, admin, spolsky etc) then storing a salt with the password immediately makes the cracking feasible.

因此,如果有一个特定的用户成为目标(root、admin、spolsky 等),那么立即存储一个带有密码的 salt 使破解成为可能。

Storing the salt away from the password increases the difficulty of the crack - not in any mathematical manner, just in difficulty of getting the salt and the password. One could envisage a seperate server that just takes plaintext, username and hash, and looks up the salt used on that users join date, and returns 1/0

将盐存储在密码之外会增加破解的难度——不是以任何数学方式,只是难以获取盐和密码。可以设想一个单独的服务器,它只接受明文、用户名和哈希值,并查找该用户加入日期使用的盐,并返回 1/0

So, in short, if you store the salt with the password and someone accesses the passwords, then depending on password strength, each password is crackable in a reasonable length of time. A different salt per user protects all the otherusers, but if you are after a specific account, that is irrelevant. If the hacker is only after one password then storing a salt with password makes the crack feasible. Keeping a rotating salt elsewhere from the password table means the hacker now needs two data steals to crack the password, because without the salt any attack is doomed to thousands of years of work.

因此,简而言之,如果您将盐与密码一起存储并且有人访问密码,那么根据密码强度,每个密码都可以在合理的时间内破解。每个用户使用不同的盐可以保护所有其他用户,但如果您使用的是特定帐户,那就无关紧要了。如果黑客只需要一个密码,那么使用密码存储盐会使破解成为可能。在密码表的其他地方保留一个旋转盐意味着黑客现在需要两次数据窃取来破解密码,因为没有盐,任何攻击都注定要进行数千年的工作。

This is all a trade off - forcing people to use 15 digit passwords means they all get post-it noted to the screen, or become "easy to remember" i.e. words.

这完全是一种权衡——强迫人们使用 15 位密码意味着他们都会在屏幕上贴上便利贴,或者变得“容易记住”,即单词。

At this point you may as well move to Kerberos or similar - if the data you are protecting is valuable the users will understand and rspect it. If not why are we bothering.

在这一点上,您也可以转向 Kerberos 或类似的 - 如果您保护的数据很有价值,用户就会理解并考虑它。如果不是,我们为什么要打扰。

But I still recommend you do notimplement your own auth mechansim. Use Kerberos, use a semi public PKI, dont roll your own. I have no idea how to tell if my server just swapped the RAM holding my salt into disk and thats only one mistake I can immediately think of in rolling my own authentication.

不过我还是建议你没有实现自己的身份验证mechansim。使用 Kerberos,使用半公开的 PKI,不要自己动手。我不知道如何判断我的服务器是否只是将保存我的盐的 RAM 交换到磁盘中,这只是我在滚动我自己的身份验证时可以立即想到的一个错误。

HTH

HTH

回答by Johan

$username = mysql_real_escape_string($username);
$password = mysql_real_escape_string($password);
$time = time();
$query = "
INSERT INTO user (name, unixcreationtime, passhash) 
VALUES ('$username', '$time', SHA2(CONCAT('$time','$password'),512) ";

Don't use SHA1, it is no longer secure.
I suggest doing all hashing in MySQL, that way you can be sure there's no difference in the outcome of the hash.

不要使用 SHA1,它不再安全。
我建议在 MySQL 中进行所有散列,这样您就可以确定散列的结果没有区别。

Select a user using:

使用以下方法选择用户:

$query = "SELECT id FROM user 
          WHERE name = '$username' 
            AND passhash = SHA2(CONCAT(creationdate,'$password'),512) ";

回答by Layke

No, because when the password gets hashed it doesn't look like fishABC09, it looks like: 5f4dcc3b5aa765d61d8327deb882cf99which is an md5 hash.

不,因为当密码被散列时fishABC09,它看起来不像 ,它看起来像:5f4dcc3b5aa765d61d8327deb882cf99这是一个 md5 散列。

To gain access to your system, even if they know the hash, it cannot be reversed. We use salts in order to add complexity to our hashes, and to prevent hash lookups in rainbow tables.

要访问您的系统,即使他们知道哈希值,也无法逆转。我们使用盐来增加散列的复杂性,并防止在彩虹表中进行散列查找。

For example: Do a Google searchfor the md5 hash of "password" which is: 5f4dcc3b5aa765d61d8327deb882cf99

例如:在Google 上搜索“password”的 md5 哈希值,即:5f4dcc3b5aa765d61d8327deb882cf99

A lot of results right?

很多结果吧?

Now I'm going to create a hash again, I will still use "password", but I will add a SALT which is "AHG(*@". I'm guessing the only response will be for this post and some bot scrapers that have read this post :)

现在我要再次创建一个散列,我仍然会使用“密码”,但我会添加一个“AHG(*@”) 的 SALT。我猜唯一的回应将是这篇文章和一些爬虫程序读过这篇文章的人:)

cbe57e92ffbb0086320891b9f979156d

Should be only a few results, or this post which are this post.

应该只有几个结果,或者这个帖子哪个是这个帖子。

Just Remember

只记得

Just remember this... hashes are one way, so even if you gain the hash, you do not know what was used to create it

请记住这一点...散列是一种方式,所以即使您获得了散列,您也不知道是用来创建它的

回答by Sam Terrell

I know this is old, but for anyone that manages to stumble on this post...

我知道这是旧的,但对于任何设法偶然发现这篇文章的人......

What you are really trying to do HMAC. Trying to do that yourself creates issues. You can partially compute hashes, which reduces the amount of effort required to guess at a password, for instance. HMAC addresses those kinds of concerns.

您真正想做的是 HMAC。自己尝试这样做会产生问题。例如,您可以部分计算哈希值,从而减少猜测密码所需的工作量。HMAC 解决了这些问题。

Better still is scrypt or bcrypt. HMAC still often uses hash algorithms that are designed to be quick and easy to compute; there is even hardware implementations of many of the hash algorithms. bcrypt is computationally expensive and scrypt is memory intensive. Both make things harder for an attacker, but scrypt in particular makes it really hard to build hardware devices to crack a password.

更好的是 scrypt 或 bcrypt。HMAC 仍然经常使用旨在快速且易于计算的哈希算法;许多散列算法甚至有硬件实现。bcrypt 的计算成本很高,而 scrypt 是内存密集型的。两者都让攻击者更难,但特别是 scrypt 使得构建硬件设备来破解密码变得非常困难。

I really like the chart over here: https://github.com/pbhogan/scrypt#why-you-should-use-scrypt

我真的很喜欢这里的图表:https: //github.com/pbhogan/scrypt#why-you-should-use-scrypt

回答by Sam Terrell

its an old topic but others will come here too so i will try to describe it very easy:

这是一个古老的话题,但其他人也会来这里,所以我会尽量简单地描述它:

if you do hash(password) you get the same hashvalue for every password [hash(password) = hash(password)]. if two users have the same password, you will see it because the hashvalues are the same. some passwords like "password" or "12345678" are taken very often so: same hashvalue in your database -> maybe password "password" or "12345678" (rainbowtable attack).

如果你做 hash(password) 你会得到相同的 hashvalue 为每个密码 [hash(password) = hash(password)]。如果两个用户有相同的密码,你会看到它,因为哈希值是相同的。一些像“password”或“12345678”这样的密码经常被使用,所以:你的数据库中的哈希值相同->可能是密码“password”或“12345678”(彩虹表攻击)。

if you hash(salt+password) you dont get the same hash for the same passwords because hash(salt1+password) is not hash(salt2+password).

如果你散列(盐+密码)你不会得到相同密码的相同散列,因为散列(盐1+密码)不是散列(盐2+密码)。

hash(x) is just a mathematical function like f(x)=y. if you put the same x you will get the same y. this function must be "special" to be safe. just dont use sha1 because it is not safe anymore :D

hash(x) 只是一个数学函数,如 f(x)=y。如果您输入相同的 x,您将获得相同的 y。这个函数必须是“特殊的”才能安全。只是不要使用 sha1,因为它不再安全了:D

回答by Atul Gupta

If a hacker gets access to your PHP files, he can simply add mail function, so whoever login, account details are emailed to hacker.

如果黑客可以访问您的 PHP 文件,他可以简单地添加邮件功能,因此无论谁登录,帐户详细信息都会通过电子邮件发送给黑客。

If hacker only gets access to database, he should not get passwords plainly written there, so crypt it before saving. Save it in md5 hash which can't be reversed.

如果黑客只能访问数据库,他不应该在那里写明密码,因此在保存之前对其进行加密。将其保存在无法逆转的 md5 哈希中。

I normally use salt based on username or userID, that PHP program know how to generate for each user along with static_site_key.

我通常使用基于用户名或用户 ID 的盐,该 PHP 程序知道如何为每个用户以及 static_site_key 生成盐。

回答by Benjamin Udink ten Cate

A salt is a random number of a fixed length. This salt must be different for each stored entry. It must be stored as clear text next to the hashed password.

盐是固定长度的随机数。对于每个存储的条目,此盐必须不同。它必须以明文形式存储在散列密码旁边。

From

https://www.owasp.org/index.php/Hashing_Java#Why_add_salt_.3F

https://www.owasp.org/index.php/Hashing_Java#Why_add_salt_.3F