密码存储 - 自加密与散列?

信息安全 加密 哈希
2021-08-09 00:42:26

自加密:使用密码本身(作为对称密钥)加密密码。基本上,通过这样做,我将获得随机数据作为输出。现在,为了从这些加密数据中检索密码,我必须知道密钥。也就是说,我必须知道密码本身。这个属性不是使它成为一种单向函数吗?

我知道散列是在数据库中存储密码的推荐和首选方式(因为它是一种方式)。我也知道我们永远不应该想出我们自己的加密货币但是,我只是想知道这种自我加密(如果使用而不是散列)在数据库中存储密码的有效性(在安全性方面)如何?

顺便说一句,我无法在互联网上找到有关此的详细信息。我在头脑风暴和搜索时提出了这个概念,我遇到了 Tom Leek 的这个答案(他将这种技术称为自我加密)。

4个回答

很有趣的想法。

但是我们这里有一个问题,常规的哈希值总是相同的,你的哈希值会根据输入的不同而有不同的大小。

因此,它可以被认为不如常规哈希安全。

哈希函数定义:

哈希函数是可用于将任意大小的数字数据映射到固定大小的数字数据的任何函数 (...)

人们在存储密码时寻找的属性是让尝试猜测原始文本变得非常乏味和缓慢,但在软件中执行一次时需要相对较快。

您也不希望两个用户使用相同的密码来生成相同的密文。为了解决这个问题,在散列的情况下你需要一个盐,或者在自我加密的情况下需要一个 IV。

正如 Herr K 已经提到的,如果您有密码,则需要从中派生密钥才能加密密码。您通常使用的是 PBKDF2。这基本上也用于密码散列。因此,通过进行自我加密,您基本上可以在 PBKDF2 之上构建另一层。

这会增加任何安全性吗?并不真地。有更好的方法,比如在 PBKDF2 函数中增加轮数,而不是在顶部构建一个自加密层。

有更好的功能实际上是建立在对称加密算法之上的,例如 bcrypt。bcrypt 建立在 Blowfish 之上。所以也许你应该选择 bcrypt,因为它已经过多年的审查。

请注意,著名的密码学家 Tom Leek 曾经说过:

散列是密码存储的正确框架(实际上,密码没有被存储,只是一个密码验证令牌)。然而,众所周知,增长自制散列函数很困难。当密码学家想要构建一个散列函数时,他们会花费大量时间,因为他们需要确保该函数是安全的,而仅仅通过查看它是无法知道的。

您在这里所做的实际上是定义一个哈希函数:给定一个加密函数 Encrypt(K, M),其中 K 是密钥,M 是要加密的明文,您定义 Hash(P) = Encrypt(P, P)。因此,您正在发明一种新的加密功能。

首先,对于密码哈希,您不需要任何旧的哈希函数,因为它需要抵抗暴力破解尝试,攻击者猜测密码可能是什么,计算哈希(猜测)并将结果与​​存储的结果进行比较哈希。无论您使用哪种哈希算法,您都需要加强它以包含盐(这样攻击者必须单独破解每个哈希,而不是一次访问所有帐户并使较弱的密码失效)并使哈希函数变慢(因为这会伤害攻击者需要做出很多错误的猜测,而不是防御者,后者将主要验证正确的尝试)。请参阅如何安全地散列密码?以获得更详细的解释。

您可以从普通哈希函数构建一个缓慢的、加盐的哈希函数,其结构类似于 SSH(P, S) = Hash(Hash(...Hash(P + S)...)) (其中 P + S 是连接)。这不一定是最好的方法——例如,一个好的密码散列函数用于典型用途应该需要大量内存,因为服务器比专门的密码破解硬件有更多的内存。但这是一个好的开始。

问题仍然是 Hash(P) = Encrypt(P, P) 是否是一个好的散列函数。这不是自动的;请注意,对密码算法安全性的一些分析依赖于密钥和明文是独立的。

一个主要困难是大多数加密算法中的密钥大小受到严格限制,通常是恒定的。例如,标准加密算法 AES 仅接受三种密钥大小(8、12 或 16 字节)。如果密码(加盐)太短,可以用无效字符填充,但如果太长怎么办?基于长于密钥大小的材料使用密钥的常用方法是……对材料应用散列函数(接受任意输入长度)。

如果要从加密算法中推导出哈希函数,其实有一个更简单的方法:不是Hash(P) = Encrypt(P, P),而是定义Hash(P) = Encrypt(P, 0),即加密一个all -zeros 明文块(或其他一些众所周知的明文块)。这就是原始的 Unix 密码散列函数所做的。这种方法优于 Encrypt(P, P) 的优点是您可以从现有的算法分析中受益:加密算法旨在抵抗已知明文攻击。密码和盐大小的限制仍然存在。

在加密中使用密码作为密钥的想法实际上与密码的存储方式非常相似。

密码存储的早期方法之一是基于将密码用作密钥的 DES。盐作为明文,密文作为哈希值。

由于多种原因,这种方法如今被认为已过时且不安全,但这种方法的优点仍然存在于较新的密码散列设计中。

基于 DES 的方法的问题:

  • 只有12位盐。我不知道为什么,因为似乎没有任何技术原因为什么它不能使用完整的 64 位块大小作为盐。
  • 密码只有 8 个字符。DES 的结构方式,密钥由 8 个字组成,每个字 7 位。使用 8 字节密码作为 DES 密钥只会忽略每个字节的最高有效位。这意味着 56 位密钥,并且前 8 位以外的任何字符都将被忽略。

如果我们看一下 MD5、SHA1 和 SHA2 的工作原理,它与分组密码非常相似。要散列的每个数据块都以与加密密钥扩展为块密码的密钥调度的方式非常相似的方式扩展。然后发生的情况如下:

一个常量(称为 IV)用作为密钥的第一块数据进行加密,然后将密文和纯文本相加。加法的结果被传递到下一轮。然后将下一个数据块用作加密上一步的输出的密钥,并再次添加纯文本和密文。这一直持续到数据+填充结束。

因此,这两种方法都将密码用作分组密码中的密钥,但该分组密码的输入不是密码。

总而言之,使用自身作为密钥来加密密码以获取密码哈希并没有从根本上破坏。但魔鬼在细节中,在细节没有明确规定之前,没有人能说它是否有弱点。特别是具有足够熵的盐和支持任意长度的密码是必须支持的两个重要方面,以便有机会匹配现代密码哈希的安全性。