9

首先,上下文:我正在尝试创建一个需要登录的基于命令行的工具(Linux)。此工具上的帐户与系统级帐户无关——这些都与 /etc/passwd 无关。

我计划使用与 /etc/passwd 相同的格式(大致)将用户帐户存储在文本文件中。

尽管不使用系统级密码文件,但与以明文形式存储密码相比,使用 crypt 似乎是一种很好的做法。(虽然 crypt 肯定比以明文形式存储密码更好,但我对其他方法持开放态度。)

我的地穴知识基于此: https ://docs.python.org/2/library/crypt.html

该文档似乎要求一些不可能的事情:“建议在检查密码时使用完整的加密密码作为盐。”

嗯?如果我正在创建加密密码(如创建用户记录时),我如何将加密密码用作盐?它还不存在。(我假设您必须使用相同的盐来创建和检查密码。)

我试过使用明文密码作为盐。这确实有效,但有两个问题;一种容易克服,一种严重:

1)明文密码的前两个字母包含在加密密码中。您可以通过不将前两个字符写入文件来解决此问题:

user_record = '%s:%s:%s' % (user_name, crypted_pw[2:], user_type)

2)通过使用明文密码作为盐,您似乎正在减少系统中的熵量。可能我误解了盐的用途。

我能够得出的最佳实践是使用用户名中的前两个字符作为盐。这是合适的,还是我错过了什么使这成为一个糟糕的举动?

我对盐的理解是它可以防止从字典中预先计算密码哈希。我可以对所有密码使用标准盐(例如我的首字母“JS”),但这对于攻击者来说似乎比使用每个用户用户名中的两个字符要少。

4

8 回答 8

7

Python 的 crypt() 是系统的 crypt() 函数的包装器。从 Linux crypt() 手册页:

char *crypt(const char *key, const char *salt);

key 是用户输入的密码。
salt 是从集合 [a–zA–Z0–9./] 中选择的两个字符的字符串。
此字符串用于扰乱 4096 之一中的算法
不同的方法。

重点是“两个字符的字符串”。现在,如果您查看 crypt() 在 Python 中的行为:

>>> crypt.crypt("Hello", "World")
'Wo5pEi/H5/mxU'
>>> crypt.crypt("Hello", "ABCDE")
'AB/uOsC7P93EI'

您会发现结果的前两个字符总是与原始盐的前两个字符重合,这确实构成了真正的双字符盐本身。也就是说, crypt() 的结果形式为 2char-salt + encrypted-pass。因此,如果不是传递两个字符的盐或原始的多字符盐,而是传递整个加密密码,则结果没有区别。

注意:集合[a–zA–Z0–9./]包含64个字符,64*64=4096。以下是两个字符与“ 4096种不同方式”的关系。

于 2008-12-01T04:20:23.523 回答
4

对于 crypt 模块的使用:

生成加密密码时,您提供盐。只要满足列出的条件,增加对暴力破解的抵抗力也可能是随机的。检查密码时,您应该提供 getpwname 中的值,以防您使用的系统支持更大的盐大小并且不是自己生成的。

普通的留言:

如果这与实际系统登录无关,那么没有什么能阻止您使用比 crypt 更强大的方法。您可以随机生成 N 个字符的每个用户 salt,以与 SHA-1 哈希中的用户密码组合。

string_to_hash = user.stored_salt + entered_password
successful_login = (sha1(string_to_hash) == user.stored_password_hash)

更新:虽然这对彩虹表要安全得多,但上述方法仍然存在加密弱点。正确应用 HMAC 算法可以进一步提高您的安全性,但这超出了我的专业范围。

于 2008-12-01T03:54:55.897 回答
3

您误解了文档;它说由于盐的长度可能会根据底层 crypt() 实现而有所不同,因此您应该在检查密码时提供整个加密密码作为盐值。也就是说,不要把前两个字符拉出来作为盐,而是把整个东西都扔进去。

您让初始盐基于用户名的想法似乎没问题。

于 2008-12-01T03:51:59.540 回答
3

以下是一些关于加盐密码的一般建议:

  1. 一般来说,salts 用于制作ranbow 表,计算成本太高。因此,您应该在所有密码哈希中添加一点随机盐,并将其以明文形式存储在哈希密码值旁边。
  2. 使用HMAC - 这是一个很好的标准,它比连接密码和盐更安全。
  3. 使用 SHA1:MD5 已损坏。如果您知道这一点,就没有冒犯的意思,只是彻底。;)

不会让盐成为密码的功能。攻击者必须生成一个彩虹表才能拥有一个即时查找密码数据库,但他们只需要这样做一次。如果您选择一个随机的 32 位整数,他们必须生成 2^32 个表,这(与确定性盐不同)会耗费太多内存(和时间)。

于 2008-12-01T04:01:13.140 回答
2

为了增加一些强度,您可以通过在格式中使用盐来让 crypt 模块使用 md5。

$1$ABCDEFGH$

其中 ABCDEFGH 是你的盐字符串。

>>> p = crypt.crypt('password', '$1$s8Ty3/f$')
>>> p
Out: '$1$s8Ty3/f$0H/M0JswK9pl3X/e.n55G1'
>>> p == crypt.crypt('password', p)
Out: True

(请注意,这是 crypt 的 gnu 扩展,请参阅 linux 系统上的“man crypt”)。MD5(现在甚至是 SHA1)可能会“损坏”,但它们对于密码哈希仍然相对较好,并且 md5 仍然是 linux 本地密码的标准。

于 2008-12-01T16:09:13.000 回答
1

密码或从密码派生的任何内容都不应用作盐。特定密码的盐应该是不可预测的。

用户名或用户名的一部分是可以容忍的,但更好的是来自加密 RNG 的随机字节。

于 2008-12-01T04:40:02.860 回答
1

使用 PBKDF2,在不同的线程上看到这个评论(包括 Python 实现)。

于 2008-12-01T19:12:07.840 回答
-2

查看Björn Edström解释的文章TrueCrypt 。它包含对 truecrypt 工作原理的易于理解的解释,以及对一些 truecrypt 功能(包括密码管理)的简单 Python 实现。

他说的是 Python crypt() 模块,而不是 Python 中的 TrueCrypt

Python 2 中的默认值crypt.crypt()不是很安全,文章解释了更安全的替代方案如何工作。

于 2008-12-07T01:01:21.220 回答