Python crypt 模块——盐的正确使用是什么?
首先,上下文:我正在尝试创建一个基于命令行的工具(Linux) 需要登录。 此工具上的帐户与以下内容无关 系统级帐户——这些都不会查看/etc/passwd。
我计划使用与 /etc/passwd 相同的格式(大致)将用户帐户存储在文本文件中。
尽管没有使用系统级密码文件,但使用 crypt 似乎 与将密码存储在中相比,这是一个很好的使用习惯 明文。 (虽然 crypt 肯定比将密码存储在 明文,我愿意接受其他方式。)
我的密码知识基于此: https://docs.python.org/2/library/crypt.html
文档似乎要求一些不可能的东西:“它 建议检查时使用全加密密码作为salt 密码。”
嗯?如果我正在创建加密密码(例如,在创建用户时 记录)如何使用加密密码作为盐? 它 尚不存在。 (我假设您必须使用相同的盐来创建和检查密码。)
我尝试使用明文密码作为盐。 这确实 工作,但有两个问题; 一个容易克服,一个严重:
1)明文密码的前两个字母包含在 加密的密码。 你可以通过不写前两个来解决这个问题 文件中的字符:
user_record = '%s:%s:%s' % (user_name, crypted_pw[2:], user_type)
2)通过使用明文密码作为盐,您似乎是 减少系统中的熵。 可能我是 误解了盐的用途。
我能够得出的最佳实践是使用前两个 用户名中的字符作为盐。 这样合适吗, 还是我错过了一些东西,导致这是一个糟糕的举动?
我对盐的理解是它可以防止预先计算密码 来自字典的哈希值。 我可以为所有人使用标准盐 密码(例如我姓名缩写“JS”),但这似乎不太重要 与使用每个用户用户名中的两个字符相比,攻击者的负担更大。
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.
I am planning to store user accounts in a text file using the same format (roughly) as /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.)
My crypt knowledge is based on this:
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:
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.
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
Python 的 crypt() 是系统 crypt() 函数的包装器。 来自 Linux crypt() 手册页:
重点是“双字符字符串”。 现在,如果您查看 Python 中 crypt() 的行为:
您会发现结果的前两个字符始终与原始盐的前两个字符一致,这确实形成了真正的双字符盐本身。
也就是说,crypt() 的结果具有 2char-salt + 加密通行证的形式。
因此,如果传递整个加密密码而不是传递两个字符盐或原始的多字符盐,结果没有区别。
注:集合[a–zA–Z0–9./]包含64个字符,64*64=4096。 以下是两个个字符与“4096种不同方式”的关系。
Python's crypt() is a wrapper for the system's crypt() function. From the Linux crypt() man page:
Emphasis is on "two-character string". Now, if you look at crypt()'s behavior in Python:
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.
Note: the set [a–zA–Z0–9./] contains 64 characters, and 64*64=4096. Here's how two characters relate to "4096 different ways".
对于 crypt 模块的使用:
当生成加密密码时,您提供盐。 只要满足列出的条件,也可以随机增加对暴力破解的抵抗力。 检查密码时,您应该提供 getpwname 中的值,以防您所在的系统支持更大的盐大小并且您自己没有生成它。
一般评论:
如果这与实际的系统登录无关,那么没有什么可以阻止您使用比 crypt 更强大的方法。 您可以随机生成 N 个字符的每个用户盐,与 SHA-1 哈希中的用户密码组合。
更新:虽然这对于彩虹表来说更加安全,但上述方法仍然存在加密弱点。 正确应用 HMAC 算法可以进一步提高您的安全性,但超出了我的专业知识范围。
For the use of the crypt module:
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.
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.
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.
您误解了文档; 它表示,由于盐的长度可能会根据底层 crypt() 实现的不同而有所不同,因此您应该在检查密码时提供整个加密密码作为盐值。。 也就是说,不要把前两个炭拉下来作为盐,而是把整个东西扔进去。
您让初始盐基于用户名的想法似乎没问题。
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.
Your idea of having the initial salt be based on the username seems okay.
以下是一些有关对密码加盐的一般建议:
我不会让盐成为密码的函数。 攻击者必须生成彩虹表才能拥有即时查找密码数据库,但他们只需要做一次。 如果您选择随机 32 位整数,它们必须生成 2^32 个表,这(与确定性盐不同)会花费太多内存(和时间)。
Here's some general advice on salting passwords:
I would not have 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).
为了增加强度,您可以通过在格式中使用盐来让 crypt 模块使用 md5。
其中 ABCDEFGH 是您的盐字符串。
(请注意,这是 crypt 的 GNU 扩展,请参阅 Linux 系统上的“man crypt”)。 MD5(现在甚至还有 SHA1)可能已经被“破解”,但它们对于密码哈希来说仍然相对较好,并且 md5 仍然是 Linux 本地密码的标准。
For some added strength, you can get the crypt module to use md5 by using a salt in the format.
where ABCDEFGH is your salt string.
(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.
密码或从密码派生的任何内容都不应该用作盐。 特定密码的盐值应该是不可预测的。
用户名或用户名的一部分是可以接受的,但更好的是来自加密 RNG 的随机字节。
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.
使用 PBKDF2,请参阅不同线程上的此评论(包括 Python 实现) )。
Use PBKDF2, see this comment on a different thread (includes Python implementation).
请参阅 Björn Edström 的文章 TrueCrypt 解释。 它包含对 truecrypt 如何工作的易于理解的解释以及 truecrypt 的一些功能包括密码管理的简单 Python 实现。
Python 2 中的默认
crypt.crypt()
不是很安全,并且 文章解释了更安全的替代方案如何发挥作用。Take a look at the article TrueCrypt explained by 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.
Default
crypt.crypt()
in Python 2 is not very secure and the article explains how more secure alternatives might work.