10

我在产品代码中遇到了以下代码片段。它使用按位异或进行字符串比较。这比String.equals(Object o)方法好吗?作者在这里想要达到什么目的?

private static boolean compareSecure(String a, String b)
  {
    if ((a == null) || (b == null)) {
      return (a == null) && (b == null);
    }
    int len = a.length();
    if (len != b.length()) {
      return false;
    }
    if (len == 0) {
      return true;
    }
    int bits = 0;
    for (int i = 0; i < len; i++) {
      bits |= a.charAt(i) ^ b.charAt(i);
    }
    return bits == 0;
  }

对于上下文,被等同的字符串是身份验证令牌。

4

1 回答 1

11

这是字符串比较函数的常见实现,不受时间攻击。

简而言之,这个想法是每次比较字符串时都比较所有字符,即使你发现其中任何一个不相等。在“标准”实现中,您只需打破第一个差异并返回 false。

这是不安全的,因为它会泄露有关比较字符串的信息。具体来说,如果左侧字符串是您要保留的秘密(例如密码),而右侧字符串是用户提供的内容,则不安全的方法允许黑客通过反复尝试相对容易地发现您的密码输出不同的字符串并测量响应时间。两个字符串中相同的字符越多,“不安全”函数就越需要比较它们。

例如,使用标准方法比较“1234567890”和“0987654321”将导致仅对第一个字符进行一次比较并返回 false,因为 1!=0。另一方面,比较“1234567890”和“1098765432”会导致执行2个比较操作,因为第一个是相等的,你必须比较第二个才能发现它们不同。这将花费更多时间并且是可以衡量的,即使我们正在谈论远程调用。

如果您使用 N 个不同的字符串进行 N 次攻击,每个字符串都以不同的字符开头,您应该会看到其中一个结果比其余的多出几分之一毫秒。这意味着第一个字符相同,因此该函数必须花费更多时间来比较第二个字符。冲洗并重复字符串中的每个位置,您可以比蛮力更快地破解秘密数量级。

防止此类攻击是此类实施的重点。

编辑:正如 Mark Rotteveel 在评论中努力指出的那样,这种实现仍然容易受到旨在揭示字符串长度的定时攻击。在许多情况下这仍然不是问题(您不关心攻击者知道长度,或者您处理标准数据并且任何人都可以知道长度,例如某种已知长度的哈希)

于 2018-05-10T12:37:54.027 回答