Python crypt 模块——盐的正确用法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/329956/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python crypt module -- what's the correct use of salts?
提问by Schof
First, context: I'm trying to create a command-line-based tool (Linux) that requires login. Accounts on this tool have nothing to do with system-level accounts -- none of this looks at /etc/passwd.
首先,上下文:我正在尝试创建一个需要登录的基于命令行的工具 (Linux)。此工具上的帐户与系统级帐户无关——这些都没有查看 /etc/passwd。
I am planning to store user accounts in a text file using the same format (roughly) as /etc/passwd.
我计划使用与 /etc/passwd 相同的格式(大致)将用户帐户存储在文本文件中。
Despite not using the system-level password files, using crypt seemed to be a good practice to use, as opposed to storing passwords in cleartext. (While crypt is certainly better than storing passwords in cleartext, I'm open to other ways of doing this.)
尽管不使用系统级密码文件,但使用 crypt 似乎是一种很好的做法,而不是以明文形式存储密码。(虽然 crypt 肯定比以明文形式存储密码更好,但我对其他方法持开放态度。)
My crypt knowledge is based on this: https://docs.python.org/2/library/crypt.html
我的 crypt 知识基于此:https: //docs.python.org/2/library/crypt.html
The documentation seems to ask for something that isn't possible: "it is recommended to use the full crypted password as salt when checking for a password."
该文档似乎要求一些不可能的事情:“建议在检查密码时使用完整的加密密码作为盐。”
Huh? If I'm creating the crypted password (as in, when creating a user record) how can I use the crypted password as a salt? It doesn't exist yet. (I'm assuming that you must use the same salt for creating and checking a password.)
嗯?如果我正在创建加密密码(如在创建用户记录时),我如何将加密密码用作盐?它还不存在。(我假设您必须使用相同的盐来创建和检查密码。)
I've tried using the plaintext password as a salt. This does work, but has two problems; one easily overcome, and one serious:
我试过使用明文密码作为盐。这确实有效,但有两个问题;一个容易克服,一个严重:
1) The first two letters of the plaintext password are included in the crypted password. You can fix this by not writing the first two characters to the file:
1) 密文密码中包含明文密码的前两个字母。您可以通过不将前两个字符写入文件来解决此问题:
user_record = '%s:%s:%s' % (user_name, crypted_pw[2:], user_type)
2) By using the plaintext password as the salt, you would seem to be reducing the amount of entropy in the system. Possibly I'm misunderstanding the purpose of the salt.
2)通过使用明文密码作为盐,您似乎减少了系统中的熵量。可能我误解了盐的用途。
The best practice I've been able to derive is to use the first two characters from the username as the salt. Would this be appropriate, or is there something I've missed that makes that a bad move?
我能够得出的最佳实践是使用用户名的前两个字符作为盐。这是否合适,还是我遗漏了什么使那是一个糟糕的举动?
My understanding of a salt is that it prevents pre-computing password hashes from a dictionary. I could use a standard salt for all passwords (such as my initials, "JS,") but that seems to be less of a burden for an attacker than using two characters from each user's username.
我对盐的理解是它可以防止从字典中预先计算密码哈希。我可以对所有密码(例如我的首字母缩写“JS”)使用标准盐,但这对攻击者来说似乎比使用每个用户用户名中的两个字符要少。
采纳答案by HUAGHAGUAH
For the use of the crypt module:
对于 crypt 模块的使用:
When GENERATING the crypted password, you provide the salt. It might as well be random to increase resistance to brute-forcing, as long as it meets the listed conditions. When CHECKING a password, you should provide the value from getpwname, in case you are on a system that supports larger salt sizes and didn't generate it yourself.
生成加密密码时,您需要提供盐。只要满足列出的条件,增加对暴力破解的抵抗力也可能是随机的。检查密码时,您应该提供 getpwname 中的值,以防您使用的系统支持更大的盐大小并且不是自己生成的。
General comments:
普通的留言:
If this has nothing to do w/ actual system logins, there is nothing preventing you from using a stronger method than crypt. You could randomly generate N characters of per-user salt, to be combined with the user's password in a SHA-1 hash.
如果这与实际系统登录无关,则没有什么可以阻止您使用比 crypt 更强大的方法。您可以为每个用户随机生成 N 个字符的盐,以在 SHA-1 哈希中与用户的密码组合。
string_to_hash = user.stored_salt + entered_password
successful_login = (sha1(string_to_hash) == user.stored_password_hash)
UPDATE: While this is far more secure against rainbow tables, the method above still has cryptographic weaknesses. Correct application of an HMAC algorithm can yet further increase your security, but is beyond my realm of expertise.
更新:虽然这对彩虹表更安全,但上述方法仍然存在加密弱点。正确应用 HMAC 算法可以进一步提高您的安全性,但这超出了我的专业领域。
回答by Federico A. Ramponi
Python's crypt() is a wrapper for the system's crypt() function. From the Linux crypt() man page:
Python 的 crypt() 是系统的 crypt() 函数的包装器。从 Linux crypt() 手册页:
char *crypt(const char *key, const char *salt); key is a user's typed password. salt is a two-character string chosen from the set [a–zA–Z0–9./]. This string is used to perturb the algorithm in one of 4096 different ways.
Emphasis is on "two-characterstring". Now, if you look at crypt()'s behavior in Python:
重点是“两字串”。现在,如果你看看 crypt() 在 Python 中的行为:
>>> crypt.crypt("Hello", "World")
'Wo5pEi/H5/mxU'
>>> crypt.crypt("Hello", "ABCDE")
'AB/uOsC7P93EI'
you discover that the first two characters of the result always coincide with the first two characters of the original salt, which indeed form the true two-character-salt itself. That is, the result of crypt() has the form 2char-salt + encrypted-pass. Hence, there is no difference in the result if instead of passing the two-character-salt or the original many-characters-salt you pass the whole encrypted password.
您发现结果的前两个字符总是与原始盐的前两个字符重合,这确实形成了真正的两字符盐本身。也就是说,crypt() 的结果具有 2char-salt + encrypted-pass 的形式。因此,如果您传递整个加密密码而不是传递两个字符的盐或原始的多字符的盐,则结果没有区别。
Note: the set [a–zA–Z0–9./] contains 64 characters, and 64*64=4096. Here's how twocharacters relate to "4096different ways".
注意:集合[a-zA-Z0-9./]包含64个字符,64*64=4096。以下是两个字符与“ 4096种不同方式”的关系。
回答by ojrac
Here's some general advice on salting passwords:
以下是一些关于加盐密码的一般建议:
- In general, salts are used to make ranbow tablestoo costly to compute. So, you should add a little randomized salt to all your password hashes, and just store it in plaintext next to the hashed password value.
- Use HMAC- it's a good standard, and it's more secure than concatenating the password and salt.
- Use SHA1: MD5 is broken. No offense intended if you knew this, just being thorough. ;)
- 通常,使用盐来制作Ranbow 表的计算成本太高。因此,您应该为所有密码哈希添加一点随机盐,并将其以明文形式存储在哈希密码值旁边。
- 使用HMAC- 这是一个很好的标准,并且比连接密码和盐更安全。
- 使用 SHA1:MD5 已损坏。如果您知道这一点,则无意冒犯,只是彻底。;)
I would nothave the salt be a function of the password. An attacker would have to generate a rainbow table to have an instant-lookup database of passwords, but they'd only have to do that once. If you choose a random 32-bit integer, they'd have to generate 2^32 tables, which (unlike a deterministic salt) costs way, way too much memory (and time).
我不会让盐成为密码的函数。攻击者必须生成一个彩虹表才能拥有一个即时查找密码数据库,但他们只需要做一次。如果您选择一个随机的 32 位整数,它们将必须生成 2^32 个表,这(与确定性盐不同)会消耗太多内存(和时间)。
回答by genehack
You're misunderstanding the documentation; it says that since the length of the salt may vary depending on the underlying crypt() implementation, you should provide the entire crypted password as the salt value when checking passwords. That is, instead of pulling the first two chars off to be the salt, just toss in the whole thing.
你误解了文档;它说,由于盐的长度可能因底层 crypt() 实现而异,因此在检查密码时,您应该提供整个加密密码作为盐值。也就是说,与其将前两个字符去掉作为盐,不如将整个东西都扔进去。
Your idea of having the initial salt be based on the username seems okay.
您将初始盐基于用户名的想法似乎没问题。
回答by JimB
For some added strength, you can get the crypt module to use md5 by using a salt in the format.
为了增加一些强度,您可以通过在格式中使用盐来让 crypt 模块使用 md5。
$ABCDEFGH$
where ABCDEFGH is your salt string.
其中 ABCDEFGH 是您的盐串。
>>> p = crypt.crypt('password', '$s8Ty3/f$')
>>> p
Out: '$s8Ty3/f##代码##H/M0JswK9pl3X/e.n55G1'
>>> p == crypt.crypt('password', p)
Out: True
(note that this is a gnu extension to crypt, see "man crypt" on a linux system). MD5 (and now even SHA1) may be "broken", but they are still relatively good for password hashes, and md5 is still the standard for linux local passwords.
(请注意,这是 crypt 的 gnu 扩展,请参阅 Linux 系统上的“man crypt”)。MD5(现在甚至是SHA1)可能会被“破解”,但它们对于密码哈希还是比较好的,而且md5仍然是linux本地密码的标准。
回答by orip
Use PBKDF2, see this commenton a different thread (includes Python implementation).
使用 PBKDF2,请参阅不同线程上的此评论(包括 Python 实现)。
回答by erickson
The password, or anything derived from the password, should never be used as salt. The salt for a particular password should be unpredictable.
密码,或从密码派生的任何东西,不应该被用作盐。特定密码的盐应该是不可预测的。
A username or part of the user name is tolerable, but even better would be random bytes from a cryptographic RNG.
用户名或用户名的一部分是可以接受的,但更好的是来自加密 RNG 的随机字节。
回答by jfs
Take a look at the article TrueCrypt explainedby Bj?rn Edstr?m. It contains easy to understand explanation of how truecrypt works and a simple Python implementation of some of truecrypt's functionality including password management.
看看Bj?rn Edstr?m解释的TrueCrypt文章。它包含对 truecrypt 如何工作的易于理解的解释,以及一些 truecrypt 功能的简单 Python 实现,包括密码管理。
He's talking about the Python crypt() module, not about TrueCrypt in Python
他在谈论 Python crypt() 模块,而不是 Python 中的 TrueCrypt
Default crypt.crypt()
in Python 2 is not very secure and the articleexplains how more secure alternatives might work.
crypt.crypt()
Python 2 中的默认值不是很安全,本文解释了更安全的替代方案如何工作。