如何在我的登录密码中添加盐?

发布于 2024-08-20 09:28:43 字数 435 浏览 1 评论 0原文

我想在我的登录系统中实现一个盐,但我对它应该如何工作有点困惑。我无法理解其背后的逻辑。我知道 md5 是一种单向算法,我遇到的所有函数似乎都将所有内容散列在一起。如果是这种情况,如何取回密码进行比较呢?我最大的问题是,对用户密码加盐如何比仅对密码进行哈希处理更安全?如果数据库遭到破坏,哈希值和盐都在数据库中。这不正是黑客所需要的吗?

我还发现了另一篇关于 SO 的帖子,其中另一位开发人员说:

“确保您的盐和算法是 与数据库分开存储

我想将盐存储在数据库中。如果我这样做,这真的是一个问题吗?

我正在寻求一些帮助来理解它是如何工作的以及最好的任何帮助都可能是编辑


: 我要感谢大家的回应和想法。尽管我现在可能更加困惑,但这对我来说无疑是一次学习经历。再次感谢你们。

I want to implement a salt into my login system but am a bit confused on how this is supposed to work. I can't understand the logic behind it. I understand md5 is a one-way algorithm and all of the functions that I have come across seem to hash everything together. If this is the case, how does one get the password back out for comparison? My biggest question is, how is salting a users' password safer than just hashing the password? If a database was ever to be compromised, the hash along with the salt is in the database. Isn't this all that a hacker would need?

I also found another post here on SO where another developer said :

"Ensure your salt and algorithm are
stored separately from the database"

I would like to store the salt in the database. Is this really a problem if I do?

I'm looking for some help on understanding how this works and also what the best practice might be. Any help is greatly appreciated.


EDIT:
I want to thank everyone for their responses and ideas. Even though I may be more confused now, it has certainly been a learning experience for me. Thanks again guys.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

尬尬 2024-08-27 09:28:44

你不会把密码拿出来进行比较。当他们尝试登录时,您对密码进行加密,并将存储的值与新加密的值进行比较。

You don't get the password out for comparison. You encrypt the password when they attempt a login and compare the stored value with the newly encrypted value.

流年里的时光 2024-08-27 09:28:44

正如您所提到的,散列算法只能单向工作(或者仅当它们足够强大时:-D)

对于您关于加盐的问题,我建议使用静态盐字符串和一些<强>来自数据库的动态数据,一旦创建后就不应改变

这是一种非常安全的存储密码的方式,因为即使数据库被泄露,黑客/破解者仍然需要获取您的密码静态字符串哈希,需要猜测如何应用所有加盐。

例如,假设您有一个包含以下列的 users 表:

id
username
password
created_at

idcreated_at< /strong> 一旦填写后就永远不应该更改..

所以当你对用户的密码进行哈希处理时,你可以做简单的事情:

<?php
    $staticSalt = '!241@kadl;ap][';
    $userPass = 'my new pass';
    // assuming $user variable is already populated with DB data
    // we will generate new hash from columns and static salt:
    $genPass = sha1($user['id'] . $userPass . $user['created_at'] . $staticSalt);
?>

我希望这个有帮助:)干杯

As you mentioned, hashing algorithms work only one-way (or only if they are strong enough :-D)

For your question about salting I would recommend to hash a password with a static salt string and some dynamic data from database, which should not change after once created

This is a very secure way of storing passwords, as even if database is compromised, hackers/crackers still need to get your static string hash and need to guess how you applied all the salting..

For example let's say you have a users table with these columns:

id
username
password
created_at

columns id and created_at after once filled should never be changed..

so when you are hashing user's password you can do as simple as:

<?php
    $staticSalt = '!241@kadl;ap][';
    $userPass = 'my new pass';
    // assuming $user variable is already populated with DB data
    // we will generate new hash from columns and static salt:
    $genPass = sha1($user['id'] . $userPass . $user['created_at'] . $staticSalt);
?>

I hope this one helps :) cheers

梦太阳 2024-08-27 09:28:44

对密码进行哈希处理的目的是对您自己的管理员保密这些密码。

1) 在数据库中保存纯文本密码就可以了,除非管理员可能使用您的密码来访问其他系统。

2) 您可以使用单个全局盐,它与密码组合(通过前置或异或),然后进行哈希处理以存储在数据库中。但这很容易受到恶意管理员和专为该盐设计的彩虹表的攻击。

3) 您可以为每个用户设置一个单独的盐:数据库将用于存储盐,以及从密码/盐组合派生的哈希值。这将防止彩虹攻击,但暴力攻击仍然是可能的。

4) 最后,您可以通过使用速度受限的硬件哈希解决方案来保密您的哈希函数。

这是你能做的最好的了。由于人性的原因,密码的域是有限的,并且容易受到暴力攻击。我们试图防止管理员掌握用户密码,然后在他们不应访问的其他系统上使用它们。

其他一些注意事项:

a) 您可以在密码/盐组合上使用 bcrypt 来减慢攻击者的暴力攻击。但由于我们任命了管理员,所以他们可以有耐心。

b) 将盐与密码散列分开并不是一种有效的防御,毕竟我们假设是管理员。

c)使用现有数据作为盐会好一点,但我怀疑现有数据是否具有与随机盐一样多的熵。

Hashing passwords is meant to keep those passwords secret from your own administrator(s).

1) Keeping plain text passwords in your database would be fine except your passwords may be used by the administrator to gain access to some other system.

2) You can use a single global salt, which is combined with the passwords (by prepending or XORing them) and then hashing for storage in the database. But that is vulnerable to a malicious administrator AND a rainbow table designed for that one salt.

3) You can have a separate salt for each user: The database will be used to store the salt, and the hash derived from the password/salt combination. This will prevent a rainbow attack, but brute force attacks will still be possible.

4) Finally, you can keep your hash function a secret by using a velocity-limited hardware hashing solution.

That is as good as you can do. Because of human nature, passwords have a limited domain and are vulnerable to brute force attacks. We are trying to prevent administrators getting a hold of user passwords, and then using them on other systems they should not have access to.

Some other notes:

a) You can use bcrypt on the password/salt combination to slow down the attacker’s brute force attack. But since we are assuming administrators, they can be patient.

b) Keeping the salt separate from the password hash is not an effective defense, we are assuming administrators after all.

c) Using existing data as a salt is a little better, but I doubt existing data has as much entropy a random salt has.

一刻暧昧 2024-08-27 09:28:44

对用户密码加盐可能比仅对密码进行哈希处理更安全,因为它可以防止预计算攻击。

例如,如果黑客可以访问您的数据库,并且密码没有加盐,那么他可以在其哈希数据库中查找哈希(请参阅 http://en.wikipedia.org/wiki/Rainbow_table) 获取原始密码。

Salting a user's password is possibly safer than just hashing the password because it can protect against precomputation attacks.

For instance, if a hacker gets access to your database, and the passwords are not salted, then he can look up the hashes in his database of hashes (see http://en.wikipedia.org/wiki/Rainbow_table) to get the original passwords.

海夕 2024-08-27 09:28:43

salt 的目的是防止攻击者通过预先计算的 彩虹表

通过简单的散列,攻击者可以计算一次这样的表(一个非常长且成本高昂的操作),然后使用它来快速查找任何站点的密码。当站点使用一种固定盐时,攻击者必须专门为该站点计算一个新表。当网站为每个用户使用不同的盐时,攻击者就可以不再费心使用彩虹表 - 他将不得不分别暴力破解每个密码。

无需单独储存盐即可获得此优点。理论上它会更安全,因为它可以弥补字典或短密码的弱点。实际上,这并不值得打扰,因为最终您需要在某处访问盐来检查密码。此外,试图将它们分开会导致系统更加复杂——系统越复杂,出现安全漏洞的机会就越多。

编辑:我的具体建议:

  • 为每个用户生成长伪随机盐并存储在数据库中
  • 理想情况下使用基于 bcrypt 的哈希
  • ,不要自己实现它,使用 现有库

The point of a salt is to prevent attackers from amortizing the cost of a brute force attack across sites (or better yet, when using a different salt for each user: all users of a site) through precomputed rainbow tables.

With plain hashing, an attacker can compute such a table once (a very long, costly operation) and then use it to quickly find passwords for any site. When a site uses one fixed salt, the attacker has to compute a new table specifically for that site. When a site uses a different salt for each user, the attacker can stop bothering with rainbow tables - he'll have to brute-force each single password separately.

Storing the salts separately is not necessary to gain this advantage. In theory it would be even more secure because it would neutralize the weakness of dictionary or short passwords. In practice, it's not worth bothering with because at the end of the day, you need access to the salts somewhere to check passwords. Also, trying to separate them would lead to more complex systems - and the more complex a system is, the more opportunities for security holes there are.

Edit: My concrete recommendations:

  • Generate long pseudorandom salt for each user and store in in the DB
  • Use a bcrypt-based hash
  • ideally, don't implement it yourself, use an existing library instead
又爬满兰若 2024-08-27 09:28:43

对于相同的输入字符串,哈希函数始终返回相同的值。假设我的用户 (Alice) 的密码是 secret。使用 md5()secret 进行哈希处理会得到以下哈希值

5ebe2294ecd0e0f08eab7690d2a6ee69

使用字典(常用单词和密码的列表)或为您提供该服务的各个网站之一,攻击者 (Mallory) 在字典中看到 5ebe2294ecd0e0f08eab7690d2a6ee69 = 秘密时,可以轻松发现密码是秘密的

在散列之前加盐的过程使得在不知道你的盐的情况下使用字典攻击变得更加困难。请考虑以下情况:

<?php
$salt = '@!#%$@#$@SADLkwod,sdaDwqksjaoidjwq@#@!';
$hash = md5($salt . 'secret');

生成的哈希现在是 b58ad809eece17322de5024d79299f8a,但 Alice 的密码仍然是秘密。现在,如果马洛里拿到了加盐哈希,她很可能在字典中找不到答案。如果她这样做了,字典就会给她错误的答案。

切勿在数据库中存储静态盐。最好将其与应用程序的配置一起存储(顺便说一句,该配置不应从网络上获得)。

如果您要使用动态盐,则需要使用数据库。使用现有有效数据的非空列来构建您的盐(基于秘密加密密钥的河豚加密用户名字符串通常在加密上是安全的)。不要使用单独的盐柱。如果您无法使用现有列,请将您的盐与哈希值合并到同一列中。例如,使用前 32 个字符作为 128 位盐,然后使用最后 40 个字符作为 160 位哈希。以下函数将生成这样的哈希值:

function seeded_sha1($string, $seed_bits) {
    if(($seed_bits % 8) != 0) {
        throw new Exception('bits must be divisible by 8');
    }

    $salt = '';
    for($i = 0; $i < $seed_bits; $i+=8) {
        $salt .= pack('c', mt_rand());
    }

    $hexsalt = unpack('h*hex', $salt);

    return $hexsalt['hex'] . sha1($salt . $string);
}

function compare_seeded_sha1($plain, $hash) {
    $sha1 = substr($hash, -40);
    $salt = pack('h*', substr($hash, 0, -40));

    $plain_hash = sha1($salt . $plain);
    return ($plain_hash == $sha1);
}

如果攻击者使用 SQL 注入进入您的数据库,至少他/她检索到的哈希值将没有用处,因为他/她无法访问您的应用程序配置。如果你的服务器被 root,那么无论你做什么,游戏都差不多结束了。

注意: md5() 这就是为什么您使用更安全的哈希算法,例如 sha1() 。或者,更好的是,使用便携式 PHP 密码哈希框架,该框架的设计考虑了安全性,并且向后兼容几乎所有 PHP 版本。

require('PasswordHash.php');

$pwdHasher = new PasswordHash(8, FALSE);

// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );

// $hash would be the $hashed stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}

An hash function always return the same value for the same input string. Let's say my user (Alice) has the password secret. Hashing secret using md5() leads to the following hash

5ebe2294ecd0e0f08eab7690d2a6ee69

Using a dictionary (a list of common words and password) or one of the various sites that offer you that service, the attacker (Mallory) can easily find out the password is secret when he sees in his dictionary that 5ebe2294ecd0e0f08eab7690d2a6ee69 = secret.

The process of salting before hashing makes it harder to use a dictionary attack without knowing your salt. Consider the following:

<?php
$salt = '@!#%$@#$@SADLkwod,sdaDwqksjaoidjwq@#@!';
$hash = md5($salt . 'secret');

The resulting hash is now b58ad809eece17322de5024d79299f8a but Alice's password is still secret. Now if Mallory gets her hands on the salted hash, chances are she will not find the answer in her dictionary. If she does, the dictionary will give her the wrong answer.

Never store a static salt in your database. Preferably store it with your application's configuration (which by the way should not be available from the web).

If you are going to use a dynamic salt, you are going to need to use the database. Use a non-null column of existing valid data to build your salt on (blowfish-encrypted string of username based on a secret encryption key is usually cryptographically secure). Do not use a separate column for the salt. If you cannot use an existing column, incorporate your salt in the same column than your hash. For example, use the first 32 characters for your 128-bits salt and then the last 40 for your 160-bits hash. The following function will generate such an hash:

function seeded_sha1($string, $seed_bits) {
    if(($seed_bits % 8) != 0) {
        throw new Exception('bits must be divisible by 8');
    }

    $salt = '';
    for($i = 0; $i < $seed_bits; $i+=8) {
        $salt .= pack('c', mt_rand());
    }

    $hexsalt = unpack('h*hex', $salt);

    return $hexsalt['hex'] . sha1($salt . $string);
}

function compare_seeded_sha1($plain, $hash) {
    $sha1 = substr($hash, -40);
    $salt = pack('h*', substr($hash, 0, -40));

    $plain_hash = sha1($salt . $plain);
    return ($plain_hash == $sha1);
}

If an attacker gets in your database using SQL injection, at least the hashes he/she retrieves won't be useful since he/she won't have access to your application configuration. If your server gets rooted, it's pretty much game-over no matter what you do.

Note: There are other types of attack possible on md5() which is why you use more secure hashing algorithm, sha1() for example. Or, even better, use the Portable PHP password hashing framework, which has been designed with security in mind and is backwards compatible with pretty much any PHP version.

require('PasswordHash.php');

$pwdHasher = new PasswordHash(8, FALSE);

// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );

// $hash would be the $hashed stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}
幻想少年梦 2024-08-27 09:28:43

忘记使用盐(部分原因是您提到的),请使用 bcrypt 代替:

有关良好的解释,请参阅:http://codahale.com/how-to-safely-store-a-password/

Forget about using salts (partly for the reason you mention), use bcrypt instead:

For a good explanation see: http://codahale.com/how-to-safely-store-a-password/

木森分化 2024-08-27 09:28:43

其他答案都很好,所以我只提出一个其他人没有提到的小问题。您不想为每个密码使用相同的盐,因为如果两个人具有相同的密码,他们将具有相同的哈希值。这暴露了攻击者可以利用的信息。

您可以对每个用户使用相同的盐,以及 Juraj 的好主意,将密码与其他不变的数据库字段(对用户来说是唯一的)结合起来。但请注意,因为此信息与密码相关联。如果您要将用户名 + 密码哈希在一起以保证唯一的哈希值,则在不创建新用户并要求他们设置新密码的情况下,您将无法更改用户名。

作为每个用户拥有唯一盐并将其与密码哈希一起存储的示例,我将指出 /etc /shadow 在典型的 Linux 系统上。

root@linux:/root# cat /etc/shadow | grep root
root:$1$oL5TTZxL$RhfGUZSbFwQN6jnX5D.Ck/:12139:0:99999:7:::

这里,oL5TTZxL 是盐,RhfGUZSbFwQN6jnX5D.Ck/ 是哈希值。在本例中,明文密码是 root,我的系统使用的哈希算法是基于 MD5 的 BSD 密码算法。 (比我的新系统有更好的哈希算法)

The other answers are good, so I'll just throw in a minor point that nobody else has mentioned. You don't want to use the same salt for every password because then if two people have the same password, they'll have the same hash. That's exposing information that an attacker can exploit.

You could use the same salt for every user along with Juraj's good idea to combine the password with other non-changing database fields (unique to a user). But watch out because this information gets tied to the password. If you were to hash the username + password together to guarantee a unique hash, you wouldn't be able to change the username without creating a new user and requiring them to set a new password.

As an example of having a unique salt per user and storing it alongside the password hash, I'll point out /etc/shadow on your typical Linux system.

root@linux:/root# cat /etc/shadow | grep root
root:$1$oL5TTZxL$RhfGUZSbFwQN6jnX5D.Ck/:12139:0:99999:7:::

Here, the oL5TTZxL is the salt and RhfGUZSbFwQN6jnX5D.Ck/ is the hash. The plain-text password is root in this case, and the hash algorithm my system uses is the MD5-based BSD password algorithm. (newer systems than mine have better hash algorithms)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文