0

我有以下代码片段:

from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
pwd_context.hash(password)

这是描述here

我不明白的是,如果它一直返回相同的散列密码而不考虑另一个 secret_key 例如散列密码值,这怎么能保证安全?

4

1 回答 1

1

您假设它一直返回相同的散列密码而不考虑另一个“秘密”(好吧,这不是真正的秘密)是错误的;如果你运行pwd_context.hash多次,你会看到这个:

>>> from passlib.context import CryptContext
>>>
>>> pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
>>> pwd_context.hash("test")
'$2b$12$0qdOrAMoK7dgySjmNbyRpOggbk.IM2vffMh8rFoITorRKabyFiElC'
>>> pwd_context.hash("test")
'$2b$12$gqaNzwTmjAQbGW/08zs4guq1xWD/g7JkWtKqE2BWo6nU1TyP37Feq'

如您所见,这两个哈希值并不相同——即使给出相同的密码也是如此。那么究竟发生了什么?

当您不提供hash明确的盐(您正在谈论的秘密“密钥”)时,将由passlib. 值得指出的是,散列与加密不同,因此没有可谈的密钥。相反,您会看到salt提到的,这是一个明文值,用于确保两次散列的相同密码会产生不同的结果(因为您实际上是在进行散列salt + password)。

那么为什么我们会得到两个不同的值呢?这salt是实际 bcrypt 值的前 22 个字符。这些字段由$-2b表示 bcrypt 分隔,12表示 12 轮,下一个字符串是为密码存储的实际结果值(salt+resulting bcrypt hash)。该字符串的前 22 个字符是哈希。

如果你给 bcrypt 一个盐而不是让它生成一个盐,你可以看到这一点(我们将在这里忽略关于小填充的可能警告,但为了展示这个概念):

>>> pwd_context.hash("test", salt="a"*22)
'$2b$12$aaaaaaaaaaaaaaaaaaaaaOm/4kNFO.mb908CDiMw1TgDxyZeDSwum'
        ^-------------------^

如果我们明确给出相同的哈希值,结果应该是相同的(以及稍后验证密码的方式):

>>> pwd_context.hash("test", salt="a"*22)
'$2b$12$aaaaaaaaaaaaaaaaaaaaaOm/4kNFO.mb908CDiMw1TgDxyZeDSwum'
>>> pwd_context.hash("test", salt="a"*22)
'$2b$12$aaaaaaaaaaaaaaaaaaaaaOm/4kNFO.mb908CDiMw1TgDxyZeDSwum'

前面的哈希也是如此:

>>> pwd_context.hash("test")
'$2b$12$gqaNzwTmjAQbGW/08zs4guq1xWD/g7JkWtKqE2BWo6nU1TyP37Feq'
        ^-------------------^

这是实际生成的盐,然后与它一起test用于创建实际的散列:

>>> pwd_context.hash("test")
'$2b$12$gqaNzwTmjAQbGW/08zs4guq1xWD/g7JkWtKqE2BWo6nU1TyP37Feq'
                             ^------------------------------^

那么,当它对每个人都清晰可见时,我们为什么要使用这种盐呢?不可能只扫描已知哈希的哈希列表 - 因为test您的列表中的值与test您要比较的列表中的值不同(因为不同的盐),您必须实际测试猜测的密码及其盐,并通过散列算法运行它们。bcrypt被明确设计为使该过程需要时间,因此您将花费更长的时间来尝试破解密码,而不仅仅是扫描 2 亿个密码的列表并在数据库中搜索已知的哈希值。

那么当计算机变得更快时你会怎么做?您增加12参数 - 这rounds- 这会增加散列算法的运行时间,希望更安全地保持更长时间(您可以试验rounds参数 to passlib.hash)。

于 2022-02-24T21:05:13.457 回答