如果我只有一个数据库列,如何存储加盐密码哈希?

发布于 2024-09-01 09:16:33 字数 1023 浏览 7 评论 0原文

我已经阅读了许多关于这个主题的问题,但是我无法理解存储密码的加盐哈希的应用实践。

让我们从一些基本规则开始:

  • 密码“foobar12”(我们讨论密码的强度)。
  • 一种语言,本次讨论的 Java 1.6
  • 一种数据库,postgreSQL、MySQL、SQL Server、Oracle

有几个选项可用于存储密码,但我想考虑一个 (1):

将使用随机盐散列的密码存储在数据库中,一列

明文存储的自动失败不公开讨论。 :) 在 SO 和其他地方找到了使用 MD5/SHA1 和使用双列的解决方案,两者都有优点和缺点。

MD5/SHA1 很简单。 Java中的MessageDigest提供了MD5、SHA1(通过现代实现中的SHA512,当然是1.6)。此外,列出的大多数 RDBMS 都提供了有关插入、更新等的 MD5 加密功能的方法。一旦人们理解了“彩虹表”和 MD5 冲突(我已经理解了这些概念),问题就会变得明显。

双列解决方案基于这样的想法:盐不需要保密(理解它)。但是,如果您的旧系统只有一 (1) 列用于密码,并且更新表和代码的成本可能太高,那么第二列会带来复杂性,这种复杂性可能并不奢侈。

但它是在单个数据库列中存储用随机盐散列的密码,我需要通过实际应用更好地理解它。

我喜欢这个解决方案有几个原因:需要盐并考虑遗留边界。这就是我迷失的地方:如果盐是随机的,并且密码加盐被散列以生成用于存储的单向值,那么系统如何永远匹配明文密码和随机盐?

我对此有理论,当我输入时,我可能会理解这个概念:给定 128 字节的随机盐和 8 字节的密码('foobar12') ,通过对随机 128 字节盐进行散列并获取原始散列(即散列密码)的子字符串,可以以编程方式删除散列中作为盐的部分。然后使用哈希算法重新进行哈希匹配...?

所以...任何愿意提供帮助的人。 :) 我很接近吗?

I've read a number of SO questions on this topic, but grokking the applied practice of storing a salted hash of a password eludes me.

Let's start with some ground rules:

  • a password, "foobar12" (we are not discussing the strength of the password).
  • a language, Java 1.6 for this discussion
  • a database, postgreSQL, MySQL, SQL Server, Oracle

Several options are available to storing the password, but I want to think about one (1):

Store the password hashed with random salt in the DB, one column

The automatic fail of plaintext storage is not open for discussion. :) Found on SO and elsewhere are solutions with MD5/SHA1 and use of dual-columns, both with pros and cons.

MD5/SHA1 is simple. MessageDigest in Java provides MD5, SHA1 (through SHA512 in modern implementations, certainly 1.6). Additionally, most RDBMSs listed provide methods for MD5 encryption functions on inserts, updates, etc. The problems become evident once one groks "rainbow tables" and MD5 collisions (and I've grokked these concepts).

Dual-column solutions rest on the idea that the salt does not need to be secret (grok it). However, a second column introduces a complexity that might not be a luxury if you have a legacy system with one (1) column for the password and the cost of updating the table and the code could be too high.

But it is storing the password hashed with a random salt in single DB column that I need to understand better, with practical application.

I like this solution for a couple of reasons: a salt is expected and considers legacy boundaries. Here's where I get lost: If the salt is random, and the password plus salt are hashed to produced a one-way value for storing, how can the system ever match a plaintext password and a new random salt?

I have theory on this, and as I type I might be grokking the concept: Given a random salt of 128 bytes and a password of 8 bytes ('foobar12'), it could be programmatically possible to remove the part of the hash that was the salt, by hashing a random 128 byte salt and getting the substring of the original hash that is the hashed password. Then re hashing to match using the hash algorithm...?

So... any takers on helping. :) Am I close?

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

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

发布评论

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

评论(5

仅此而已 2024-09-08 09:16:33

没有什么大秘密。单列解决方案与多列解决方案类似,只是它将盐和哈希组合到单个列中。检查代码仍然必须知道如何将该单个值分解为盐和散列。 (这就是加盐密码的典型工作方式 - 例如,UNIX /etc/shadow 格式将算法标识符、加盐和哈希值一起存储在单个字段中)。

不过,您不必太担心这一点,因为密码散列算法应该包含执行此操作的智能。例如,如果您使用 jBCrypt,那么您只需:

  • 存储 存储密码时数据库密码列中的BCrypt.hashpw(); 检查密码时,将
  • 数据库密码列中的值作为第二个参数提供给 BCrypt.checkpw()。

There's no great mystery. The single-column solution is just like the multi-column solution, except that it combines the salt and the hash together into a single column. The checking code still has to know how to break that single value down into the salt and hash. (This has been how salted passwords have typically worked - for example, the UNIX /etc/shadow format stores an algorithm identifier, salt and hash together in a single field).

You don't have to worry about this too much though, because the password hashing algorithm should include the smarts to do this. For example, if you use jBCrypt, then you simply:

  • Store the string returned by BCrypt.hashpw() in the database password column when storing a password; and
  • Supply the value from the database password column as the second parameter to BCrypt.checkpw() when checking a password.
≈。彩虹 2024-09-08 09:16:33

您还可以将盐和哈希存储在同一列中(使用分隔符)。

You could also store salt and hash in the same column (using a separator).

陌上芳菲 2024-09-08 09:16:33

您始终必须通过将盐存储在数据库中(正如您在多列解决方案中看到的那样)来了解盐,或者能够以其他方式生成盐(这会破坏随机盐的一些但不是全部点)。

如果您只有一列来存储密码,那么您可以:

  • 动态生成盐(即不是真正随机的,而是使用用户名或电子邮件地址的某种函数)
  • 将盐与散列值连接起来在存储密码之前以某种方式存储密码。

在第一种情况下,您可以想出一些简单的函数:

public String getSalt(String username)
{
  // assuming Hash returns a String
  return Hash(username + " 1234 my site is totally awesome").substring(0,16);
}

在第二种情况下:

// Passwords stored in the db as 16 characters of salt, and the rest is password hash
public boolean authenticate(String username, String authPassword)
{
  // 'SELECT saltyhash FROM users WHERE username=x'
  String saltyhash = getSaltyHashForUserFromDB(username);
  String salt = saltyhash.substring(0,16);

  String dbPassword = salt + Hash(salt + authPassword);

  // perform the actual 'SELECT FROM users WHERE saltypassword=x' stuff
  return hitTheDatabaseToPerformLogin(username, dbPassword);
}

public void createUser(String username, String password)
{
  String salt = createSomeAwesomeSalt();
  String saltyhash = salt + Hash(salt + password);
  createTheUserInTheDatabase(username, saltyhash);
}

You always have to know the salt, by storing it in the DB (as you saw with multi column solutions) or be able to generate it in some other way (which defeats some, but not all, of the point of random salt).

If you only have a single column in which to store the password, then you can either:

  • generate the salt on the fly (ie not really random, but use some sort of function of the username or email address)
  • concatenate the salt with the hashed password in some way before storing it.

In the first case, you can come up with some trivial function:

public String getSalt(String username)
{
  // assuming Hash returns a String
  return Hash(username + " 1234 my site is totally awesome").substring(0,16);
}

In the second:

// Passwords stored in the db as 16 characters of salt, and the rest is password hash
public boolean authenticate(String username, String authPassword)
{
  // 'SELECT saltyhash FROM users WHERE username=x'
  String saltyhash = getSaltyHashForUserFromDB(username);
  String salt = saltyhash.substring(0,16);

  String dbPassword = salt + Hash(salt + authPassword);

  // perform the actual 'SELECT FROM users WHERE saltypassword=x' stuff
  return hitTheDatabaseToPerformLogin(username, dbPassword);
}

public void createUser(String username, String password)
{
  String salt = createSomeAwesomeSalt();
  String saltyhash = salt + Hash(salt + password);
  createTheUserInTheDatabase(username, saltyhash);
}
人海汹涌 2024-09-08 09:16:33

这就是我迷路的地方:如果盐
是随机的并经过哈希处理
密码,系统怎么可能
与密码匹配吗?

它通过计算用户输入的密码的哈希值来匹配它,使用相同的盐,它从数据库中读取(与哈希值同时读取)。

Here's where I get lost: if the salt
is random and hashed with the
password, how can the system ever
match the password?

It matches it by computing the hash of the password the user entered with the same salt, which it reads from the database (at the same time as the hash).

來不及說愛妳 2024-09-08 09:16:33

请参阅 http://www.aspheute.com/english/20040105.asp

用户使用加盐哈希进行身份验证,而不是未加盐的密码或随机盐本身。加盐哈希和盐(但不是实际密码)都存储在数据库中(如果您愿意,可以将它们存储在单列中,但在使用前必须再次将它们分开)。

为了从用户那里恢复加盐哈希(以便您可以将其与存储的加盐哈希进行比较),您需要来自数据库的加盐以及用户提供的密码。

加盐哈希的创建方式如下:

  // Initialize the Password class with the password and salt
  Password pwd = new Password(myPassword, mySalt);

  // Compute the salted hash
  // NOTE: you store the salt and the salted hash in the database
  string saltedHash = pwd.ComputeSaltedHash();

身份验证的完成方式如下:

// retrieve salted hash and salt from user database, based on username
...

Password pwd = new Password(txtPassword.Text, nSaltFromDatabase);

if (pwd.ComputeSaltedHash() == storedSaltedHash)
{
   // user is authenticated successfully
}
else
{
...

为每个用户生成一个新的盐。如果两个用户意外选择相同的密码,则两个用户帐户的加盐哈希值仍然不同。

See http://www.aspheute.com/english/20040105.asp

The user is authenticated with a salted hash, not the unsalted password or the random salt by itself. The salted hash and the salt (but not the actual password) are both stored in the database (you can store them in a single column if you like, but you will have to separate them again before use).

In order to recover the salted hash from the user (so that you can compare it to the stored salted hash), you need the salt from the database, and the password provided by the user.

The salted hash is created like this:

  // Initialize the Password class with the password and salt
  Password pwd = new Password(myPassword, mySalt);

  // Compute the salted hash
  // NOTE: you store the salt and the salted hash in the database
  string saltedHash = pwd.ComputeSaltedHash();

Authentication is done like this:

// retrieve salted hash and salt from user database, based on username
...

Password pwd = new Password(txtPassword.Text, nSaltFromDatabase);

if (pwd.ComputeSaltedHash() == storedSaltedHash)
{
   // user is authenticated successfully
}
else
{
...

A new salt is generated for each user. Should two users accidentally choose the same password, the salted hash will still be different for both user accounts.

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