安全存储过去使用/生成的密码是个好主意吗?

信息安全 密码 哈希
2021-09-08 04:59:09

假设一个组织每周生成约 100 个密码;这些密码长 32 个字符,随机生成需要生成密码的顾问使用密码生成器

  • 根据要求生成 20 个密码
  • 然后顾问从 20 个建议中选择一个

我们的“安全顾问”的一项要求是防止生成的密码重复使用,例如保证每个密码的唯一性。我被要求找到一个解决方案,虽然我知道我必须使用 bcrypt 来存储每个生成的密码的哈希值,但我个人认为这是一个坏主意。控制了 ever-generated-pw-db(尽管只是希望安全的哈希)的攻击者有一个很好的字典。

有没有关于如何安全地执行此操作(存储所有生成的密码)的解决方案?

是否有一个 KILLER 参数(例如用于简单哈希的彩虹表)说这种解决方案从安全 POV 来看是不好的?

3个回答

让我们做数学。我喜欢数学。

您在大小为n = 62 32的空间中生成密码(在大小为 62的字母表中随机、统一且相互独立地选择 32 个字符,即所有大写字母、小写字母和数字)。假设你已经生成了k 个这样的密码;k很容易估算,因为您每周将产生大约 2000 个潜在密码(100 个“真实密码”,但每个密码都是从 20 个新的随机密码中选择的)。

很容易看出,k仍将远小于n的平方根(实际上,k在大约 457 亿亿年后才在n的平方根附近)。然后,我们可以将您遇到冲突(即相同的候选密码出现两次)的概率近似为k 2 /2n左右。假设你让你的系统运行一百年(这已经很荒谬了),即大约 5218 周,这产生接近2.4·10 -44的概率。

现在将其与您的安全顾问被从动物园逃脱的大猩猩伤害的概率进行比较;这个概率至少是每天10 -18 (我之前做过这个比较,然后做了一次,因为我喜欢这个例子;实际概率高于这个,但我们保守一点)。因此,平均而言,在发生碰撞之前,您的安全顾问将被至少4.7·10 25访问凶残的大猩猩,即四千七千万亿只大猩猩(由于这远远超过了活着的大猩猩的数量,不得不假设它们中的一些会来几次)。如果顾问仍然担心碰撞而不是大猩猩,那么您已经证明该顾问并不比狒狒聪明。

总而言之,很容易确保不重复使用密码:宇宙已经做到了。通过不做任何特别的事情,您已经确保没有碰撞,其成功率比您在保护您的安全顾问(和他的孩子)免受大猩猩攻击方面的成功率高数十亿(数十亿倍)(而且您可能已经在这样做了)在这方面也做得很好)。


现在让我们假设你的安全顾问对数学不感兴趣,因为他更相信漂亮的图片而不是漂亮的数字(在这方面,他与普通黑猩猩没有什么不同,所以至少这里有某种讽刺的一致性) . 建立一个过滤“重复”的系统将是浪费金钱,而且至关重要的是,这是一种易于演示的方法(例如对那个“安全顾问”的老板)浪费钱(一定要让他知道这个事实);然而,他可能仍然坚持这样做,因为他首先提出了它,并且违背他自己过去的话会危及他感知到的阿尔法男性地位(再次,黑猩猩浮现在脑海中)。不幸的是,这在人类身上经常发生。那么你的工作,在适当地警告你的等级制度的完全空洞之后(留下书面痕迹!当有超过三个脑细胞的人进行审计时,他们会保护你),是确保,至少,这个副本——检测系统不会削弱系统。重复检测很浪费;让它不要有害。

可能的密码空间非常大,比2 190多一点。所有密码都是等概率的,所以你有大约 190 位的熵,这是巨大的. 对于此类密码,您不需要良好的密码散列,因为密码散列函数(盐、迭代......)的所有功能都是应对“正常”密码固有弱点的方法,即它们具有低熵的事实,因此可以被暴力破解(这称为字典攻击)。但是,您的密码具有太多的熵,以至于暴力破解甚至无法远程应用它们(即使您以某种方式雇用大猩猩来尝试密码,在您用完可能的密码之前,您也会用完大猩猩)。因此,简单的散列将起作用:将散列值存储在数据库中,这将允许您进行快速的“已使用”检查。

但是,有些系统通过实际散列它们并将散列值用作“密码”来“使用密码”。尤其是 Windows:当它使用密码时,它实际上对密码计算的 MD4 哈希值(128 位)起作用。因此,如果您存储散列密码并且碰巧使用与其他密码消耗系统相同的散列函数,那么您实际上存储的是“明文”密码,这很糟糕。

为避免此问题,请使用“您自己的”哈希函数,这实际上是指HMAC散列过程如下所示(在 C#/.NET 中):

using System.Security.Cryptography;
using System.Text;

// ...
static byte[] hmacKey = Encoding.UTF8.GetBytes("my security advisor is a chimpanzee");

static byte[] HashPassword(string password)
{
    HMAC h = HMAC.Create();
    h.Key = hmacKey;
    return h.ComputeHash(Encoding.UTF8.GetBytes(password));
}

任何其他系统不太可能使用相同的密码散列函数(即具有相同密钥的 HMAC),因此将这些“散列”值存储在数据库中是安全的,并且允许重复检测。


现在是杀手锏:为什么,哦,为什么,你想检测重复项?检测重复的系统本质上也是一个可以判断正在使用(或曾经)使用的密码的系统。这样的系统只会损害安全性:它可以帮助攻击者了解给定密码在系统上是否“值得尝试”。幸运的是,在您的情况下,可能的密码空间的绝对大小可以避免这种伤害。然而,总的来说,坚持检测重复也坚持削弱密码提供的保护。这很糟糕,近乎恶意。

用一些谨慎的措辞暗示安全顾问试图植入后门相对容易(当然,不是一个很好的后门,因为他不擅长,但在刑事案件中,意图才是最重要的) . 例如,考虑以下设置:您设计一个系统,帮助用户为其银行智能卡生成 4 位 PIN 码。智能在三个错误的 PIN 码后会自行封锁,因此偷卡的攻击者只有 1/3333 的概率猜到它。但是,如果有一个系统可以生成随机 PIN 码并避免重复那么攻击者可以劫持系统(例如用一些 SQL 注入攻击转储其数据库),然后让它生成 9999 个 PIN 码,这些都将与智能卡用户获得的 PIN 码不同。这揭示了用户的 PIN:它是 10000 个可能的 PIN 码中唯一一个防复制系统不会生成的。

这就是它的核心:当防止密码重复具有任何可检测的效果时(这里不是你的情况,因为在你的一生中甚至在太阳的一生中都不会发生重复),那么这种效果是负面的。这是一个坏主意。有恶作剧的味道。

您的“安全顾问”提供他的要求的具体原因是什么?如果他不能证明这个要求是合理的,请让他推开(当然以礼貌的方式)。

想想看,你正在使用 CSPRNG。使用 62^32 种可能的组合,您不太可能生成重复密码。您正在将宝贵的磁盘空间浪费在收益的事情上。

如果您必须实现此要求,只需像存储任何普通密码一样存储它们。bcrypt是一个强哈希。如果攻击者设法破坏一个包含 32 个字符长的随机生成密码的数据库,他将一无所获。他很有可能会浪费大量的计算能力来尝试破解它(如果你喜欢无缘无故地浪费攻击者的时间,这可能被认为是一种好处)。

此设置中的最佳选择是存储所有以前的密码哈希。另外,请不要使用 bcrypt。它既好又安全,但是新系统(关心安全边际)应该采用不同的技术,例如 scrypt。实际上看起来也将很快被替换。

更多信息(一个非常好的综述):http ://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html