密码哈希比较中的定时攻击

信息安全 验证 密码 哈希 爪哇 定时攻击
2021-09-08 06:40:30

我今天和一位朋友讨论了他的密码哈希比较。我认为你不能在你遇到的第一个哈希不匹配时返回 false,我给他发了一个链接,指向一篇关于 Java 6 中发生的 Java 计时攻击的文章。

public static boolean isEqual(byte digesta[], byte digestb[]) {
    for (int i = 0; i < digesta.length; i++) {
        if (digesta[i] != digestb[i]) {
            return false;
        }
    }
    return true;
}

我的版本,我认为是正确的:

public static boolean isEqual(byte digesta[], byte digestb[]) {
    boolean isEquals = true;   
    //this avoid a possible timing attack
    for (int i = 0; i < digesta.length; i++) {
        if (digesta[i] != digestb[i]) {
            isEquals = false;
        }
    }
    return isEquals;
}

对我来说,这表明可能存在时间攻击,因为如果哈希中存在不匹配,则返回 false。他认为这不会对应用程序构成风险,重要的是密码是加盐和散列的,这可能不会影响应用程序的安全性。

我是否对我的应用程序的安全性感到偏执?

4个回答

第一种算法对定时攻击很敏感,而第二种看起来更好(但我不确定它是否易受攻击)。然而,在第二个版本中存在一个潜在的安全隐患:如果两个字符串的长度不同会发生什么?

定时攻击是一个真正值得担心的安全问题,因此您提出这个问题是正确的。我可以部分同意您的朋友的观点,因为使用盐和良好的散列算法更为重要。然而,这并不意味着定时攻击不重要或不应该被认真对待。他们应该。

然而,在这种情况下,攻击者如何发动定时攻击并不明显。由于输入(密码)是用盐进行散列的,攻击者可能无法自由控制任何比较的散列将是什么。这意味着可能无法搜索匹配哈希的方式。但这一切都取决于整个系统是如何构建的,而不仅仅是字符串比较算法。

处理所有这些的一种好方法是使用一个好的库来进行散列。一个编写良好的库应该为您处理加盐和防御定时攻击,而无需您自己编写所有代码。

你们都是对的,但你们都错过了重点:)

你是对的:这是时间弱点的典型例子,并可能造成侧信道攻击。

您的编码器是正确的:考虑到网络传输时间和您自然放入的其他缓解因素(蛮力检测、锁定等),漏洞利用的可能性很小。

但是,这不是我主要关心的问题。这是一个计时漏洞的经典例子,AKA 这是一个非常基本的安全漏洞。因此,我假设编写此代码的人在管理密码和哈希方面并不像他们想象的那样经验丰富。这就引出了一个非常自然的问题:他们在不知不觉中还犯了什么错误?

如果没有该领域专家的完整代码/系统审查,您将永远不会知道该问题的答案。因此,我建议您接受@Ander 的建议:切换到标准库。

您展示的第二段代码仍然容易受到计时攻击。这是因为 if-branch 被采用多少次,以及采用什么顺序,取决于输入数据。

这是一种可以抵抗任何合理 CPU 上的定时攻击的算法:

public static boolean isEqual(byte digesta[], byte digestb[]) {
    if (digesta.length != digestb.length)
        return false;
    int difference = 0;
    for (int i = 0; i < digesta.length; i++)
        difference |= digesta[i] ^ digestb[i];
    return difference == 0;
}

你是对的。因为您编写了代码来逐个字符地检查哈希字符(为什么要这样做?),所以可以使用计时来逐个字符地计算出正确的哈希字符。但这与尝试随机密码没有什么不同。您只知道您的哪些尝试导致了关闭哈希。它不会通知您的下一个猜测。

除此之外,您还应该拥有许多其他针对暴力攻击的保护措施,这并不是一个大威胁。