我有一个LAMP(PHP)网站正在变得流行 .
我通过将用户密码存储为md5哈希来保证安全 .
但我现在看到这不安全;我应该腌制md5哈希 - 因为它目前可以使用彩虹表解码未加盐的md5哈希 .
我能做什么?
我不想让每个人都输入新密码 .
您可以通过在表中添加列来迁移密码以存储新格式 .
当用户成功登录时,如果新列为空,请将更强的密码放在那里并清空原始列 . 如果新列有一个条目,请将输入与其中的值进行比较 .
这里有两个选择
自己解码密码,并用盐重新编码(我推荐的东西比MD5更有趣) . 您应该通知用户您可能也需要花费很多时间 .
让他们重新输入密码,并存储盐渍和加密的密码 .
据我所知,没有其他方法可以恢复密码 .
编辑:虽然MD5是一个哈希值,不应该是可解码的,但它可以使用彩虹表来破解:概率几乎为1,你可以找到一个唯一的(这里是概率)字符串,最多比如20个带有给定哈希的字符,特别是如果你的字符集有限,比如说是字母数字 . 严格来说,这不是解码 . 出于所有实际目的,它是 . 额外注意:制作彩虹表,查找1000密码仍然需要花费很多时间 .
您可以执行“两步散列”,而不是一步创建散列 .
您可以将每个密码哈希附加到用户名,然后再次哈希 . 这将创建一个不可加密的哈希值,该哈希值具有独特的信息 .
通常的腌制过程是
salt PWD - >哈希
你可以这样做:PWD - > Hash - > UserID Hash - > Hash
(注意,UserID仅被选中,因此每个双哈希存在一个唯一的盐...随意让你的盐变得更复杂)
你可以在飞行中加盐 . 添加一段代码,以便当有人登录时,它执行正常过程(计算密码的MD5总和并根据存储的哈希检查它),如果成功,则从清除中重新计算哈希的盐渍版本输入的文本密码,并将其存储在密码文件中 .
唯一的缺点是你需要添加一个指示器来确定每个MD5是否被腌制,因为你可以混合使用一段时间 . 或者,对于轻微的安全性损失,您可以检查每个密码和未加密的密码,如果有任何一个命中,请接受登录 . 当然,如果您检测到它是未加盐的,那么您在此时进行升级 .
答案很简单,请确保在新的哈希系统中保留记录或某种用户标记的标记,下次登录时,验证它们,计算新哈希,翻转标记 .
现在,只要有人登录并设置了标志,请使用新哈希对其进行身份验证 .
为什么不在用户表中添加新列 new_pwd ,该列存储 md5($originallyHashOfPwd . $salt) 的结果 . 然后,您可以预先计算 new_pwd ,一旦完成,请调整您的登录检查以将 md5(md5($entered_pwd) . $salt) 的结果与 new_pwd 中的结果进行比较 . 完成切换登录检查后,删除旧列 .
new_pwd
md5($originallyHashOfPwd . $salt)
md5(md5($entered_pwd) . $salt)
这应该会阻止彩虹表式攻击 .
你仍然可以使用盐 . 只需从当前哈希中计算另一个哈希值和salt:
$newHash = md5($salt.$oldHash);
对于新密码,您需要使用:
$hash = md5($salt.md5($password));
更新密码同时使密码更安全的一种好方法是更改为使用盐渍SHA1密码 . SHA1更难创建碰撞,并且它与MD5的字符串长度也不同 . MD5长度为32个字符,而SHA1长度为40个字符 .
要在PHP中转换它们,首先要检查存储密码的字符串长度 . 如果长度为32个字符,请使用旧方法检查密码,然后使用SHA1将新密码写入数据库 .
如果我没记错的话,这正是WordPress处理这个问题的方式 .
当用户下次登录时动态地重新加密密码,即首先检查密码是否正确,然后用盐加密并再次存储 .
如其他人所述,将原始哈希加盐 . 这里只需几点:
盐越长越好 . 如果它们包含更多,那么只需要[a-z0-9],但首先长度更好 .
如果某人已经拥有您的数据库的副本并且您使用salt重新使用相同的密码,则使用salt重新散列旧的哈希将不起作用 . 相反,你真的应该强迫用户制作一个新密码 .
您应该将新密码(和要加密的密码)与最常用密码的各种列表相匹配 . 这些用于"brute force"攻击 . 提示/强制用户更改密码 .
如果你正在远离MD5,那么你应该跳过简单的腌制,然后采用一种更好的技术,称为拉伸 . 特别是你应该使用bcrypt(用PHP实现为PHPASS) .
这是一个很好的链接,为什么bcrypt:http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html
以下是如何:1 . 下载phpass包:http://www.openwall.com/phpass/ 2.查看test.php以获取如下示例:
require 'PasswordHash.php'; $t_hasher = new PasswordHash(8, FALSE); $correct = 'plaintextpassword'; $hash = $t_hasher->HashPassword($correct); $check = $t_hasher->CheckPassword($correct, $hash);
如果$ check === true(上面就是这种情况)那么密码是正确的 . 如果你的密码是'hello',你可以使用HashPassword散列它,将散列放在数据库中,当用户登录时,调用CheckPassword(userenteredpassword,hashInDb)来查看密码是否正确
遗憾的是,您唯一的方法是告诉用户更新密码 .
你也可以生成随机密码,但这也是同样的麻烦 .
edit
您可以对存储的密码进行双重编码 . 所以你的新盐渍哈希算法将是:
md5(md5($new_password).$salt).':'.$salt
更新旧密码使用
md5($old_password.$salt).':'.$salt
检查提供的密码是否正确只需使用
list($stored_password, $salt) = explode(':', $salted_password); if(md5(md5($provided_password).$salt) == $stored_password) { // you are now logged in }
12 回答
您可以通过在表中添加列来迁移密码以存储新格式 .
当用户成功登录时,如果新列为空,请将更强的密码放在那里并清空原始列 . 如果新列有一个条目,请将输入与其中的值进行比较 .
这里有两个选择
自己解码密码,并用盐重新编码(我推荐的东西比MD5更有趣) . 您应该通知用户您可能也需要花费很多时间 .
让他们重新输入密码,并存储盐渍和加密的密码 .
据我所知,没有其他方法可以恢复密码 .
编辑:虽然MD5是一个哈希值,不应该是可解码的,但它可以使用彩虹表来破解:概率几乎为1,你可以找到一个唯一的(这里是概率)字符串,最多比如20个带有给定哈希的字符,特别是如果你的字符集有限,比如说是字母数字 . 严格来说,这不是解码 . 出于所有实际目的,它是 . 额外注意:制作彩虹表,查找1000密码仍然需要花费很多时间 .
您可以执行“两步散列”,而不是一步创建散列 .
您可以将每个密码哈希附加到用户名,然后再次哈希 . 这将创建一个不可加密的哈希值,该哈希值具有独特的信息 .
通常的腌制过程是
salt PWD - >哈希
你可以这样做:PWD - > Hash - > UserID Hash - > Hash
(注意,UserID仅被选中,因此每个双哈希存在一个唯一的盐...随意让你的盐变得更复杂)
你可以在飞行中加盐 . 添加一段代码,以便当有人登录时,它执行正常过程(计算密码的MD5总和并根据存储的哈希检查它),如果成功,则从清除中重新计算哈希的盐渍版本输入的文本密码,并将其存储在密码文件中 .
唯一的缺点是你需要添加一个指示器来确定每个MD5是否被腌制,因为你可以混合使用一段时间 . 或者,对于轻微的安全性损失,您可以检查每个密码和未加密的密码,如果有任何一个命中,请接受登录 . 当然,如果您检测到它是未加盐的,那么您在此时进行升级 .
答案很简单,请确保在新的哈希系统中保留记录或某种用户标记的标记,下次登录时,验证它们,计算新哈希,翻转标记 .
现在,只要有人登录并设置了标志,请使用新哈希对其进行身份验证 .
为什么不在用户表中添加新列
new_pwd
,该列存储md5($originallyHashOfPwd . $salt)
的结果 . 然后,您可以预先计算new_pwd
,一旦完成,请调整您的登录检查以将md5(md5($entered_pwd) . $salt)
的结果与new_pwd
中的结果进行比较 . 完成切换登录检查后,删除旧列 .这应该会阻止彩虹表式攻击 .
你仍然可以使用盐 . 只需从当前哈希中计算另一个哈希值和salt:
对于新密码,您需要使用:
更新密码同时使密码更安全的一种好方法是更改为使用盐渍SHA1密码 . SHA1更难创建碰撞,并且它与MD5的字符串长度也不同 . MD5长度为32个字符,而SHA1长度为40个字符 .
要在PHP中转换它们,首先要检查存储密码的字符串长度 . 如果长度为32个字符,请使用旧方法检查密码,然后使用SHA1将新密码写入数据库 .
如果我没记错的话,这正是WordPress处理这个问题的方式 .
当用户下次登录时动态地重新加密密码,即首先检查密码是否正确,然后用盐加密并再次存储 .
如其他人所述,将原始哈希加盐 . 这里只需几点:
盐越长越好 . 如果它们包含更多,那么只需要[a-z0-9],但首先长度更好 .
如果某人已经拥有您的数据库的副本并且您使用salt重新使用相同的密码,则使用salt重新散列旧的哈希将不起作用 . 相反,你真的应该强迫用户制作一个新密码 .
您应该将新密码(和要加密的密码)与最常用密码的各种列表相匹配 . 这些用于"brute force"攻击 . 提示/强制用户更改密码 .
如果你正在远离MD5,那么你应该跳过简单的腌制,然后采用一种更好的技术,称为拉伸 . 特别是你应该使用bcrypt(用PHP实现为PHPASS) .
这是一个很好的链接,为什么bcrypt:http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html
以下是如何:1 . 下载phpass包:http://www.openwall.com/phpass/ 2.查看test.php以获取如下示例:
如果$ check === true(上面就是这种情况)那么密码是正确的 . 如果你的密码是'hello',你可以使用HashPassword散列它,将散列放在数据库中,当用户登录时,调用CheckPassword(userenteredpassword,hashInDb)来查看密码是否正确
遗憾的是,您唯一的方法是告诉用户更新密码 .
你也可以生成随机密码,但这也是同样的麻烦 .
edit
您可以对存储的密码进行双重编码 . 所以你的新盐渍哈希算法将是:
更新旧密码使用
检查提供的密码是否正确只需使用