Web 应用程序 - 存储密码
我错过了什么吗?是否有任何其他步骤将密码存储到数据库?
存储密码:
在对这个主题进行了尽可能多的研究之后,我得出的结论是,在 Web 应用程序数据库(在我的例子中是 MySQL+PHP)中存储用户密码的最佳方法如下
- :全站静态盐。 (16 rand chars incl 0-9,az,AZ,[]/*-')
- 为每个用户分配一个随机盐(存储在数据库中)。
- 存储结果 hash_function($userPassword + $sitewideSalt + $randomSalt)
- 将 $randomSalt 与结果哈希一起存储。
- 使用bcrypt可调工作负载 哈希
攻击#1: 攻击者通过 SQL 注入转储数据库。
数据库结果 我们的 hash_function 和随机数 用户盐。转储后,攻击者可以 获取 $userPassword 并 $randomSalt 通过查找他自己的帐户。然后通过猜测哈希值 诸如 md5 之类的函数他可以启动 彩虹攻击 $sitewideSalt。但这可能需要 1.41 亿 世纪[1]。
通过使用这种类型的安全性不允许数据库转储危及存储的密码。用户仍然必须通过另一种方法找到$sitewideSalt。
攻击#2:攻击者发现本地文件包含( LFI) 向量。
攻击者可以获得我们 Web 应用程序的原始代码。
通过可能的 LFI 来利用 Web 应用程序之后 或 RFI[2] 攻击者读取 我们网站的源代码 应用程序并获得我们简单的 算法和存储
$sitewideSalt。
下一步去哪里?
现在攻击者拥有了两种盐,他可以开始彩虹获取实际密码。但他必须为每个用户制作 1 个彩虹表,因为每个用户都有不同的随机用户特定盐 ($randomSalt)。
“现代服务器可以计算MD5 每秒约 330MB 的哈希值。如果 您的用户的密码是 小写字母、字母数字和 6 字符长,你可以尝试每一个 该大小的单一可能密码 大约 40 秒内。"
"...CUDA,您可以组建自己的小型超级计算机集群,它可以让您每秒尝试大约 700,000,000 个密码..."[3]
什么我们现在需要做的是使用耗时的算法(例如 bcrypt)来扩展哈希函数,bcrypt 的工作负载因子可以是较简单的算法的 5-6 个数量级。破解一个密码可能需要几年而不是几分钟,而且 bcrypt 已经为每个哈希生成了一个随机盐并将其存储在生成的哈希中
- 。 haystack.htm">http://www.grc.com/haystack.htm
- http://www.wildcardsecurity.com/security101/index.php?title=Local_File_Inclusion
Have I missed anything? Are there any additional steps storing passwords to the DB?
Storing the Password:
After as much research on the subject as possible I've come to the conclusion that the best way to store user passwords in a web application DB (in my case MySQL+PHP) is as follows:
- Assign a sitewide static salt. (16 rand chars incl 0-9,a-z,A-Z,[]/*-')
- Assign a per user random salt (stored in the DB).
- Store the result hash_function($userPassword + $sitewideSalt + $randomSalt)
- Store the $randomSalt alongside the resulting hash.
- Use bcrypt adjustable workload
hashing
Attack #1: Attacker dumps the DB via SQL Injection.
DB results of
our hash_function and the random per
user salt.After the dump the attacker could
obtain $userPassword and
$randomSalt by looking up his own account. Then by guessing the hash
function such as md5 he could start a
rainbow attack on the
$sitewideSalt. But that could take up to 1.41 hundred million
centuries[1].By using this type of security does not allow a dump of the DB to compromise stored passwords. The user still has to find the $sitewideSalt through another method.
Attack #2: Attacker finds a Local File Inclusion (LFI) vector.
Attacker could obtain the raw code for our web application.After exploiting the web application through a possible LFI
or RFI[2] the attacker reads the
source code for our web
application and obtains our simple
algorithm and the stored
$sitewideSalt.
Where to next?
Now the attacker has both of the salts he can begin to rainbow to obtain the actual passwords. Except he must make 1 rainbow table for each user as each user has a different random user specific salt ($randomSalt).
"A modern server can calculate the MD5
hash of about 330MB every second. If
your users have passwords which are
lowercase, alphanumeric, and 6
characters long, you can try every
single possible password of that size
in around 40 seconds."
"...CUDA, you can put together your own little supercomputer cluster which will let you try around 700,000,000 passwords a second..."[3]
What we need to do now is extend the hashing function by using a time consuming algorithm such as bcrypt. bcrypt's work load factor can be 5-6 orders of magnitude that of the simpler hashing functions. Cracking just one password can take years instead of minutes. And as a bonus bcrypt already generates a random salt for each hash and stores it in the resulting hash.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
干得好!对我来说看起来非常完整。
我唯一的建议是:
轮换服务盐。
设计一种方法来定期轮换服务范围的盐,并定期进行锻炼。
例如,生成新的服务盐后,将其用于所有新帐户和新帐户。任何密码更改。当现有用户尝试登录时,使用旧服务盐对其进行身份验证。如果成功,请使用新的服务盐(以及可选的新的用户特定盐)更新其哈希值。对于“一段时间”没有登录的用户,为他们随机生成一个新密码。这将为放弃您网站的用户“保持”安全,迫使那些返回的用户使用密码重置工具。 (“某个时间”=您觉得舒服的任何时间段)。
不要对您的服务盐进行硬编码。
不要让 LFI 攻击损害您的服务盐。在启动时将服务盐提供给您的应用程序,并将其保留在内存中。为了破坏服务盐,攻击者需要能够执行代码以从内存中读取盐。如果攻击者能够做到这一点,那么无论如何,你都已经受到了很好的打击。 =)
不要重复使用用户的盐。
寻找机会为用户提供新的盐。用户更改密码?生成新的随机盐。如果攻击者能够随时获取他的哈希值,这会进一步阻碍你对服务器范围的盐进行暴力破解。再加上定期轮换你的服务盐,我敢打赌你对暴力破解有很强的威慑力。
(如果其他人有其他想法,请将其标记为社区维基)。
Nice work! Looks very complete to me.
Only suggestions I would have are:
Rotate the service salt.
Design a method to periodically rotate the service-wide salt, and exercise it regularly.
For example, after generating a new service salt, use it for all new accounts & any password changes. When an existing user tries to log in, authenticate them with the old service salt. If successful, update their hash with the new service salt (and optionally a new user-specific salt). For users who don't log in for 'some time', randomly generate a new password on their behalf. This will 'keep up' security for users who've abandoned your site, forcing those that return to use the password reset facilities. ('some time' = whatever period you're comfortable with).
Don't hard-code your service salt.
Don't allow a LFI attack to compromise your service salt. Feed the service-salt to your application at start up, and keep it in memory. To compromise the service salt, an attacker would need to be able to execute code to read the salt from memory. If an attacker can do that, you're pretty well hosed anyway. =)
Don't reuse a users salt.
Look for opportunities to give users new salts. User changes his password? Generate a new random salt. This further hampers brute forcing your server-wide salt should an attacker be able to obtain his hash whenever he feels like it. Couple this with regularly rotating your service-salt, and I'd wager you've got a strong deterrent against brute-forcing.
(Marking this as a community wiki should others have additional ideas).
使用 BCrypt 处理密码是唯一的步骤,或者更确切地说,包含以下步骤:
您还忘记了此链接:http://codahale.com/how-to- safe-store-a-password/ 这是您在报价中引用的内容。
Using BCrypt to handle passwords is the only step, or rather, encompasses the following:
You also forgot this link: http://codahale.com/how-to-safely-store-a-password/ which is what you reference with the quote.