Python crypt模块——盐的正确用法是什么?

2024-04-29 07:37:03 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

尽管没有使用系统级密码文件,使用crypt似乎 作为一种良好的使用习惯,而不是将密码存储在 明文。(虽然crypt肯定比将密码存储在 cleartext,我对其他方式持开放态度。)

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

文档似乎要求一些不可能的东西:“它 建议在检查时使用完全加密的密码作为salt 获取密码。“

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

我试过把明文密码当作盐用。这是真的 工作,但有两个问题;一个很容易克服,一个很严重:

1)明文密码的前两个字母包含在 密码加密。你可以不写前两个来解决这个问题 文件中的字符:

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

2)使用明文密码作为盐,您似乎 减少系统中的熵。可能是我 误会盐的作用。

我得到的最佳实践是使用前两个 用户名中的字符作为salt。这合适吗, 或者是我错过了什么让这件事变得很糟糕?

我对salt的理解是它防止预计算密码 字典里的散列。我可以用标准盐 密码(比如我的首字母“JS”),但这似乎不是 攻击者的负担比使用每个用户用户名中的两个字符更重。


Tags: 工具命令行用户密码linux系统etc帐户
3条回答

对于crypt模块的使用:

生成加密密码时,您提供salt。只要满足所列条件,就可以随机增加对暴力的抵抗力。在检查密码时,您应该提供getpwname中的值,以防您所在的系统支持更大的盐量,而不是自己生成。

一般性意见:

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

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

更新:虽然这对于rainbow表来说更加安全,但是上面的方法仍然有密码弱点。正确应用HMAC算法可以进一步提高您的安全性,但超出了我的专业领域。

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

你认为最初的salt是以用户名为基础的,这似乎没问题。

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.

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

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

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

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

相关问题 更多 >