我们如何在 PHP 中创建相当安全的密码哈希?
我一直在阅读有关密码散列的内容,但我读过的所有论坛都充满了人们争论其背后的理论的帖子,而我并不真正理解。
我有一个旧的(可能非常弱)密码脚本,内容如下: $hash = sha1($pass1);
function createSalt()
{
$string = md5(uniqid(rand(), true));
return substr($string, 0, 3);
}
$salt = createSalt();
$hash = sha1($salt . $hash);
如果我理解正确的话,盐越长,黑客为了破解哈希值必须生成的表就越大。如果我错了,请纠正我。
我正在寻找编写一个更安全的新脚本,并且我认为这样的事情就可以了:
function createSalt()
{
$string = hash('sha256', uniqid(rand(), true));
return $string;
}
$hash = hash('sha256', $password);
$salt = createSalt();
$secret_server_hash = 'ac1d81c5f99fdfc6758f21010be4c673878079fdc8f144394030687374f185ad';
$salt2 = hash('sha256', $salt);
$hash = $salt2 . $hash . $secret_server_hash;
$hash = hash('sha512', $hash );
这更安全吗?这是否有明显的开销?
最重要的是,是否有更好的方法来确保我的数据库中的密码无法(实际上)通过密码分析恢复,从而确保安全性受到损害的唯一方法是通过我自己的编码错误?
编辑:
在阅读了您的所有答案并进一步研究后,我决定继续实施 bcrypt 方法来保护我的密码。话虽这么说,出于好奇,如果我采用上面的代码并对其进行循环(例如 100,000 次迭代),是否会实现与 bcrypt 的强度/安全性类似的效果?
I have been reading up on password hashing, but all the forums I read are full of posts from people debating theory behind it that I don't really understand.
I have an old (and presumably extremely weak) password script that reads like this:
$hash = sha1($pass1);
function createSalt()
{
$string = md5(uniqid(rand(), true));
return substr($string, 0, 3);
}
$salt = createSalt();
$hash = sha1($salt . $hash);
If I understand correctly, the longer the salt, the larger the table the hacker has to generate in order to break the hash. Please correct me if I am wrong.
I am looking to write a new script that is more secure, and I am thinking that something like this would be okay:
function createSalt()
{
$string = hash('sha256', uniqid(rand(), true));
return $string;
}
$hash = hash('sha256', $password);
$salt = createSalt();
$secret_server_hash = 'ac1d81c5f99fdfc6758f21010be4c673878079fdc8f144394030687374f185ad';
$salt2 = hash('sha256', $salt);
$hash = $salt2 . $hash . $secret_server_hash;
$hash = hash('sha512', $hash );
Is this more secure? Does this have a noticeable amount of overhead?
Most importantly, is there some better way to make sure that the passwords in my database cannot be (realistically) recovered by cryptanalysis, thus ensuring that the only way security will be compromised is through my own error in coding?
EDIT:
Upon reading all of your answers and further reasearching, I have decided to go ahead and implement the bcrypt method of protecting my passwords. That being said, for curiosity's sake, if I were to take my above code and put a loop on it for say, 100,000 iterations, would that accomplish something similar to the strength/security of bcrypt?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
盐只能帮助你到目前为止。如果您使用的哈希算法非常快,以至于生成彩虹表的成本几乎为零,那么您的安全性仍然会受到损害。
几点提示:
为什么不使用由安全专业人员开发的算法,而不是部署您自己的(本质上有缺陷的)哈希/盐算法?
使用bcrypt。它正是为此而开发的。它的缓慢性和多轮性确保攻击者必须部署大量资金和硬件才能破解您的密码。添加每个密码的盐(bcrypt 需要盐),您可以确定,如果没有大量的资金或硬件,攻击实际上是不可行的。
非便携式模式下的便携式 PHP 哈希框架允许您轻松使用 bcrypt 生成哈希值。
您还可以使用
crypt()
函数生成输入字符串的 bcrypt 哈希值。如果您沿着这条路线走下去,请确保每个哈希生成一个盐。此类可以自动生成盐并根据输入验证现有哈希值。
您可以这样使用此代码:
Salts can only help you so far. If the hashing algorithm you use is so fast that there is little to no cost for generating rainbow tables, your security is still compromised.
A few pointers:
Instead of deploying your own (inherently with flaws) hash/salt algorithm, why not use one that was developed by security professionals?
Use bcrypt. It's been developed exactly for this in mind. It slowness and multiple rounds ensures that an attacker must deploy massive funds and hardware to be able to crack your passwords. Add to that per-password salts (bcrypt REQUIRES salts) and you can be sure that an attack is virtually unfeasible without either ludicrous amount of funds or hardware.
The Portable PHP Hashing Framework in non-portable mode allows you to generate hashes using bcrypt easily.
You can also use
crypt()
function to generate bcrypt hashes of input strings. If you go down that route, make sure you generate one salt per hash.This class can automatically generate salts and verify existing hashes against an input.
You may use this code as such:
关于盐值
是的,这是正确的。尽管如果有人试图破坏只有一个用户的哈希值,盐值是没有用的。盐对于防止(减慢)攻击者对所有用户哈希值进行字典攻击非常有用。
让我用一个例子来解释一下。假设您的系统中有 3 个用户,并且您不使用 salt 值,那么您的数据库将如下所示:
现在让我们假设攻击者获得了您的数据库的副本。他现在可以通过以下方式进行字典攻击:
因此,他只需调用一次哈希函数即可检查 3 个用户之一是否拥有密码
possible_password
。不,假设您将与盐值组合的哈希值保存在数据库中,如下所示:
攻击者再次复制您的数据库。但现在为了查看
possible_password
是否被 3 个用户之一使用,他必须执行以下检查:如您所见,在这种情况下,攻击者的速度减慢了 3 倍(数字你系统中的用户数),因为他必须散列 3 个不同的字符串。这是盐值背后的一般思想,您可以在 wikipedia 上阅读更多内容。
但就你而言,盐太大了。您想要防止的是 2 个不同的用户哈希具有相同的盐值。因此,例如 2 位长度的盐可能不是一个好主意(对于超过 4 个用户,将确保 2 个用户具有相同的盐值)。无论如何,超过 48 位 的盐值就足够了。
另外,这里对盐进行哈希处理并没有什么意义
$salt2 = hash('sha256', $salt);
,这可能会以某种方式减慢速度,但总的来说会增加更多的复杂性在处理安全性时,您的系统被认为是糟糕的。常规
最后,在处理安全问题时,在代码中使用特定值永远不会好,例如 $secret_server_hash ,应始终避免此类常量值。
最好使用 SHA-2,而不是 MD5,因为近年来一些安全性 MD5 中已发现漏洞(尽管尚未真正实用)。
所以我会做这样的事情:
然后将
$hash
保存在数据库中。无论如何,正如一些用户已经指出的那样。您应该更好地使用众所周知的库,而不是创建自己的安全函数(这是学习安全性的好方法),这些库经过了更多人的测试,因此可能更安全。在你的情况下,你应该看看
crypt
它可以满足你的需要。About Salt Values
Yes, that's correct. Although if someone tries to break a hash of only one user, salt values are useless. Salts are useful for preventing (slowing down) attackers doing dictionary attack on all of your users hash values.
Let me explain that with an example. Suppose you have 3 users in your system and you don't use a salt value, so your database would like this:
Now let's assume that an attacker achieves to get a copy of your database. He could now do a dictionary attack by doing:
And so, he could check if one of the 3 users has the password
possible_password
by only calling the hash function one time.No suppose you save the hash values which were combined with salt values in your database like this:
And again an attacker copies your database. But now in order to see if
possible_password
is used by one of the 3 users he must do the following checks:As you see, in this case the attacker is slowed down by a factor of 3 (the number of users in your system), as he must hash 3 diffferent strings. That's the general idea behind salt values, you could read more on wikipedia.
But in your case, the salt is too big. What you want to prevent is 2 different user hashes to have the same salt value. So, for example a salt of 2 bits length won't probably be a good idea (for more than 4 users it will be sure that 2 have the same salt value). Anyway, a salt value of more than 48 bits will be enough.
Also, there is not really a point in hashing the salt here
$salt2 = hash('sha256', $salt);
, this could slow things somehow, but in general adding more complexity in your system is considered bad when dealing with security.General
Finally, it's never good to have specific values in your code when dealing with security, like
$secret_server_hash
, such constant values should always be avoided.It's better that you use SHA-2, instead of MD5, because in the recent years some security vulnerabilities have been found in MD5 (although they are not yet really practical).
So I would do something like this:
And then save the
$hash
at your database.Anyway, as some users already pointed out. Instead of creating your own security functions (which is a good way for learning about security) you should better use well-known libraries which are tested by a greater amount of people and hence probably more secure. In your case you should have a look at
crypt
which does what you need.确实没有必要尝试实现您自己的一系列哈希值。下面是一个实现 bcrypt 的简单类:
使用:
bcrypt 的优点是可以使计算密码的哈希值变得昂贵(通过
$cost_factor
)。这使得尝试通过暴力恢复整个数据库的密码变得不切实际。There's really no need to try to implement your own series of hashes. Here's a simple class that implements bcrypt:
Using:
The upside of bcrypt is that you can make it computationally expensive to hash a password (via
$cost_factor
). This makes it impractical to try to recover an entire databases' passwords by brute force.