Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
The community reviewed whether to reopen this question 2 years ago and left it closed:
Original close reason(s) were not resolved
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(14)
编辑(2016):使用 Argon2,scrypt, bcrypt 或 PBKDF2,按优先顺序排列。 根据您的情况使用尽可能大的减速因子。 使用经过审查的现有实施。 确保使用适当的盐(尽管您使用的库应该为您确保这一点)。
当您对密码进行哈希处理时,请使用请勿使用 PLAIN MD5。
使用PBKDF2,这基本上意味着使用随机盐来防止rainbow table 攻击,并迭代(重新散列)足够的时间来减慢散列速度 - 不是那么多您的应用程序花费的时间太长,但足以让暴力破解大量不同密码的攻击者注意到
来自文档:
Python 中的示例实现,使用 SHA-256 作为安全哈希:
编辑:正如 Eli Collins 提到的,这不是 PBKDF2 实现。 您应该更喜欢遵循标准的实现,例如 PassLib。
EDIT (2016): use Argon2, scrypt, bcrypt, or PBKDF2, in that order of preference. Use as large a slowdown factor as is feasible for your situation. Use a vetted existing implementation. Make sure you use a proper salt (although the libraries you're using should be making sure of this for you).
When you hash the passwords use DO NOT USE PLAIN MD5.
Use PBKDF2, which basically means using a random salt to prevent rainbow table attacks, and iterating (re-hashing) enough times to slow the hashing down - not so much that your application takes too long, but enough that an attacker brute-forcing a large number of different password will notice
From the document:
Example implementation in Python, using SHA-256 as the secure hash:
EDIT: as mentioned by Eli Collins this is not a PBKDF2 implementation. You should prefer implementations which stick to the standard, such as PassLib.
基本策略是使用密钥派生函数用一些盐“散列”密码。 盐和哈希结果存储在数据库中。 当用户输入密码时,盐及其输入会以相同的方式进行哈希处理,并与存储的值进行比较。 如果它们匹配,则用户通过身份验证。
细节决定成败。 首先,很大程度上取决于所选择的哈希算法。 像 PBKDF2 这样的密钥派生算法基于基于哈希的消息身份验证代码,使得查找将产生给定输出(攻击者在数据库中找到的内容)的输入(在本例中为密码)“在计算上不可行” )。
预先计算的字典攻击使用预先计算的索引或字典,从哈希输出到密码。 散列很慢(或者应该是这样),因此攻击者对所有可能的密码进行一次散列,并以给定散列的方式存储索引结果,他可以查找相应的密码。 这是空间与时间的经典权衡。 由于密码列表可能很大,因此有多种方法可以调整权衡(例如彩虹表),以便攻击者可以放弃一点速度来节省大量空间。
使用“加密盐”可以阻止预计算攻击。 这是一些使用密码进行哈希处理的数据。 它不需要是秘密,它只需要对于给定的密码来说是不可预测的。 对于每个盐值,攻击者都需要一个新的字典。 如果使用 1 个字节的 salt,攻击者需要 256 个字典副本,每个副本都使用不同的 salt 生成。 首先,他使用盐来查找正确的字典,然后使用哈希输出来查找可用的密码。 但如果添加 4 个字节呢? 现在他需要40亿本词典。 通过使用足够大的盐,可以防止字典攻击。 实际上,来自加密质量随机数生成器的 8 到 16 字节数据可以构成良好的盐。
通过预先计算,攻击者可以在每次尝试时计算哈希值。 现在找到密码需要多长时间完全取决于哈希候选密码需要多长时间。 该时间通过哈希函数的迭代而增加。 迭代次数一般是密钥导出函数的参数; 如今,许多移动设备使用 10,000 到 20,000 次迭代,而服务器可能使用 100,000 次或更多。 (bcrypt 算法使用术语“成本因子”,它是所需时间的对数度量。)
The basic strategy is to use a key derivation function to "hash" the password with some salt. The salt and the hash result are stored in the database. When a user inputs a password, the salt and their input are hashed in the same way and compared to the stored value. If they match, the user is authenticated.
The devil is in the details. First, a lot depends on the hash algorithm that is chosen. A key derivation algorithm like PBKDF2, based on a hash-based message authentication code, makes it "computationally infeasible" to find an input (in this case, a password) that will produce a given output (what an attacker has found in the database).
A pre-computed dictionary attack uses pre-computed index, or dictionary, from hash outputs to passwords. Hashing is slow (or it's supposed to be, anyway), so the attacker hashes all of the likely passwords once, and stores the result indexed in such a way that given a hash, he can lookup a corresponding password. This is a classic tradeoff of space for time. Since password lists can be huge, there are ways to tune the tradeoff (like rainbow tables), so that an attacker can give up a little speed to save a lot of space.
Pre-computation attacks are thwarted by using "cryptographic salt". This is some data that is hashed with the password. It doesn't need to be a secret, it just needs to be unpredictable for a given password. For each value of salt, an attacker would need a new dictionary. If you use one byte of salt, an attacker needs 256 copies of their dictionary, each generated with a different salt. First, he'd use the salt to lookup the correct dictionary, then he'd use the hash output to look up a usable password. But what if you add 4 bytes? Now he needs 4 billion copies of the the dictionary. By using a large enough salt, a dictionary attack is precluded. In practice, 8 to 16 bytes of data from a cryptographic quality random number generator makes a good salt.
With pre-computation off the table, an attacker has compute the hash on each attempt. How long it takes to find a password now depends entirely on how long it takes to hash a candidate. This time is increased by iteration of the hash function. The number iterations is generally a parameter of the key derivation function; today, a lot of mobile devices use 10,000 to 20,000 iterations, while a server might use 100,000 or more. (The bcrypt algorithm uses the term "cost factor", which is a logarithmic measure of the time required.)
我想你必须在数据库中添加一列来存储加密密码,然后对所有记录运行批处理作业,获取当前密码,对其进行加密(正如其他人提到的,像 md5 这样的散列是非常标准的编辑:但不应该单独使用 - 请参阅其他答案以进行良好的讨论),将其存储在新列中并检查一切是否顺利进行。
然后,您将需要更新前端,以在登录时对用户输入的密码进行哈希处理,并与存储的哈希值进行验证,而不是检查明文与明文。
在我看来,谨慎的做法是,在最终完全删除明文密码之前,将这两列保留一段时间,以确保没有发生任何意外。
还不要忘记,每当访问密码时,代码都必须更改,例如密码更改/提醒请求。 当然,您将无法通过电子邮件发送忘记的密码,但这并不是坏事。 您将不得不使用密码重置系统。
编辑:
最后一点,您可能需要考虑避免我第一次尝试测试台安全登录网站时所犯的错误:
处理用户密码时,请考虑散列发生的位置。 在我的例子中,哈希值是由在网络服务器上运行的 PHP 代码计算的,但密码是以明文形式从用户的计算机传输到页面的! 这在我工作的环境中是可以的,因为它无论如何都在 https 系统(uni 网络)内。 但是,在现实世界中,我想您可能希望在密码离开用户系统之前使用 JavaScript 等对密码进行哈希处理,然后将哈希值传输到您的网站。
I would imagine you will have to add a column to the database for the encrypted password then run a batch job over all records which gets the current password, encrypts it (as others have mentiond a hash like md5 is pretty standard edit: but should not be used on its own - see other answers for good discussions), stores it in the new column and checks it all happened smoothly.
Then you will need to update your front-end to hash the user-entered password at login time and verify that vs the stored hash, rather than checking plaintext-vs-plaintext.
It would seem prudent to me to leave both columns in place for a little while to ensure that nothing hinky has gone on, before eventually removing the plaintext passwords all-together.
Don't forget also that anytime the password is acessed the code will have to change, such as password change / reminder requests. You will of course lose the ability to email out forgotten passwords, but this is no bad thing. You will have to use a password reset system instead.
Edit:
One final point, you might want to consider avoiding the error I made on my first attempt at a test-bed secure login website:
When processing the user password, consider where the hashing takes place. In my case the hash was calculated by the PHP code running on the webserver, but the password was transmitted to the page from the user's machine in plaintext! This was ok(ish) in the environment I was working in, as it was inside an https system anyway (uni network). But, in the real world I imagine you would want to hash the password before it leaves the user system, using javascript etc. and then transmit the hash to your site.
遵循 Xan 的建议,将当前密码列保留一段时间,以便如果情况变坏,您可以快速轻松地回滚。
至于加密您的密码:
请参阅 Thomas Ptacek 的 彩虹表已经足够了:关于安全密码方案您需要了解的内容了解一些详细信息。
Follow Xan's advice of keeping the current password column around for a while so if things go bad, you can rollback quick-n-easy.
As far as encrypting your passwords:
See Thomas Ptacek's Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes for some details.
我认为您应该执行以下操作:
I think you should do the following:
正如其他人提到的,如果可以帮助的话,您就不想解密。 标准的最佳实践是使用单向哈希进行加密,然后当用户登录哈希时与其密码进行比较。
否则,您将不得不使用强加密来加密然后解密。 仅当政治原因很强烈时,我才会建议这样做(例如,您的用户习惯于致电服务台来检索密码,并且您受到来自高层的强大压力,要求您不要更改密码)。 在这种情况下,我将从加密开始,然后开始构建一个业务案例以转向哈希。
As the others mentioned, you don't want to decrypt if you can help it. Standard best practice is to encrypt using a one-way hash, and then when the user logs in hash their password to compare it.
Otherwise you'll have to use a strong encryption to encrypt and then decrypt. I'd only recommend this if the political reasons are strong (for example, your users are used to being able to call the help desk to retrieve their password, and you have strong pressure from the top not to change that). In that case, I'd start with encryption and then start building a business case to move to hashing.
出于身份验证的目的,您应该避免使用可逆加密来存储密码,即您应该只存储密码哈希并根据您存储的哈希检查用户提供的密码的哈希。 然而,这种方法有一个缺点:如果攻击者掌握,它很容易受到 rainbow table 攻击您的密码存储数据库。
您应该做的是存储预先选择的(和秘密的)盐值+密码的哈希值。 即,连接盐和密码,散列结果,并存储该散列。 进行身份验证时,执行相同的操作 - 连接盐值和用户提供的密码、哈希值,然后检查是否相等。 这使得彩虹表攻击变得不可行。
当然,如果用户通过网络发送密码(例如,如果您正在开发 Web 或客户端服务器应用程序),那么您不应该以明文形式发送密码,因此不应存储 hash(salt +密码),您应该存储并检查散列(盐+散列(密码)),并让您的客户端对用户提供的密码进行预散列并通过网络发送该密码。 如果用户(许多人这样做)出于多种目的重复使用相同的密码,这也可以保护您的用户的密码。
For authentication purposes you should avoid storing the passwords using reversible encryption, i.e. you should only store the password hash and check the hash of the user-supplied password against the hash you have stored. However, that approach has a drawback: it's vulnerable to rainbow table attacks, should an attacker get hold of your password store database.
What you should do is store the hashes of a pre-chosen (and secret) salt value + the password. I.e., concatenate the salt and the password, hash the result, and store this hash. When authenticating, do the same - concatenate your salt value and the user-supplied password, hash, then check for equality. This makes rainbow table attacks unfeasible.
Of course, if the user send passwords across the network (for example, if you're working on a web or client-server application), then you should not send the password in clear text across, so instead of storing hash(salt + password) you should store and check against hash(salt + hash(password)), and have your client pre-hash the user-supplied password and send that one across the network. This protects your user's password as well, should the user (as many do) re-use the same password for multiple purposes.
步骤 1:将加密字段添加到数据库
步骤 2:更改代码,以便在更改密码时更新两个字段,但登录仍使用旧字段。
第 3 步:运行脚本以填充所有新字段。
第 4 步:更改代码,以便登录时使用新字段,并且更改密码会停止更新旧字段。
步骤 5:从数据库中删除未加密的密码。
这应该允许您在不中断最终用户的情况下完成转换。
还:
我要做的就是将新的数据库字段命名为与密码完全无关的名称,例如“LastSessionID”或类似无聊的名称。 然后,无需删除密码字段,只需填充随机数据的哈希值即可。 然后,如果您的数据库遭到破坏,他们可以花费所有时间尝试解密“密码”字段。
这实际上可能不会完成任何事情,但是想想有人坐在那里试图找出毫无价值的信息是很有趣的
Step 1: Add encrypted field to database
Step 2: Change code so that when password is changed, it updates both fields but logging in still uses old field.
Step 3: Run script to populate all the new fields.
Step 4: Change code so that logging in uses new field and changing passwords stops updating old field.
Step 5: Remove unencrypted passwords from database.
This should allow you to accomplish the changeover without interruption to the end user.
Also:
Something I would do is name the new database field something that is completely unrelated to password like "LastSessionID" or something similarly boring. Then instead of removing the password field, just populate with hashes of random data. Then, if your database ever gets compromised, they can spend all the time they want trying to decrypt the "password" field.
This may not actually accomplish anything, but it's fun thinking about someone sitting there trying to figure out worthless information
与所有安全决策一样,需要权衡。 如果您对密码进行哈希处理(这可能是最简单的操作),您将无法提供返回原始密码的密码检索功能,您的员工也无法查找某人的密码以访问其帐户。
您可以使用对称加密,但它有其自身的安全缺陷。 (如果您的服务器被泄露,对称加密密钥也可能被泄露)。
您可以使用公钥加密,并在单独的计算机上运行密码检索/客户服务,该计算机将私钥与 Web 应用程序隔离存储。 这是最安全的,但需要两台机器架构,并且可能需要中间有防火墙。
As with all security decisions, there are tradeoffs. If you hash the password, which is probably your easiest move, you can't offer a password retrieval function that returns the original password, nor can your staff look up a person's password in order to access their account.
You can use symmetric encryption, which has its own security drawbacks. (If your server is compromised, the symmetric encryption key may be compromised also).
You can use public-key encryption, and run password retrieval/customer service on a separate machine which stores the private key in isolation from the web application. This is the most secure, but requires a two-machine architecture, and probably a firewall in between.
MD5 和 SHA1 表现出了一些弱点(两个单词可能会产生相同的哈希值),因此建议使用 SHA256-SHA512/迭代哈希值对密码进行哈希处理。
我会用应用程序编写的语言编写一个小程序,该程序会生成每个用户唯一的随机盐和密码的哈希值。 我倾向于使用相同语言作为验证的原因是,不同的加密库做的事情可能略有不同(即填充),因此使用相同的库来生成哈希并验证它可以消除这种风险。 如果您愿意,该应用程序还可以在更新表后验证登录,因为它仍然知道纯文本密码。
MD5 and SHA1 have shown a bit of weakness (two words can result in the same hash) so using SHA256-SHA512 / iterative hashes is recommended to hash the password.
I would write a small program in the language that the application is written in that goes and generates a random salt that is unique for each user and a hash of the password. The reason I tend to use the same language as the verification is that different crypto libraries can do things slightly differently (i.e. padding) so using the same library to generate the hash and verify it eliminates that risk. This application could also then verify the login after the table has been updated if you want as it knows the plain text password still.
我想对 发布的很棒的 python 示例提出一项改进奥里普。 我将 random_bytes 函数重新定义为:
当然,您必须导入 os 模块。 os.urandom 函数提供了可以在加密应用程序中安全使用的随机字节序列。 有关更多详细信息,请参阅此函数的参考帮助。
I would like to suggest one improvement to the great python example posted by Orip. I would redefine the
random_bytes
function to be:Of course, you would have to import the
os
module. Theos.urandom
function provides a random sequence of bytes that can be safely used in cryptographic applications. See the reference help of this function for further details.要对密码进行哈希处理,您可以使用 HashBytes 函数。 返回 varbinary,因此您必须创建一个新列,然后删除旧的 varchar 列。
然后
的查询来修改代码以验证密码。
您可以使用类似where是用户输入的值。
To hash the password you can use the HashBytes function. Returns a varbinary, so you'd have to create a new column and then delete the old varchar one.
Like
Then you modify the code to validate the password, using a query like
where <password> is the value entered by the user.
我不是安全专家,但我认为当前的建议是使用 bcrypt/blowfish 或 SHA-2 变体,而不是 MD5 / SHA1。
也许您还需要考虑全面的安全审核
I'm not a security expert, but i htink the current recommendation is to use bcrypt/blowfish or a SHA-2 variant, not MD5 / SHA1.
Probably you need to think in terms of a full security audit, too