我在产品代码中发现了以下代码片段。它使用按位 XOR 进行字符串比较。这比 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;
}
对于上下文,等同的字符串是身份验证 token 。
最佳答案
这是一种不受时序攻击影响的字符串比较函数的常见实现。
简而言之,想法是每次比较字符串时都比较所有字符,即使您发现其中任何字符不相等。在“标准”实现中,您只需打破第一个差异并返回 false。
这是不安全的,因为它泄露了有关比较字符串的信息。具体来说,如果左侧字符串是您想要保留的 secret (例如密码),而右侧字符串是用户提供的内容,则不安全的方法允许黑客通过反复尝试相对轻松地破解您的密码输出不同的字符串并测量响应时间。 两个字符串中相同的字符越多,“不安全”函数比较它们所需的时间就越多。
例如,使用标准方法比较“1234567890”和“0987654321”将导致仅对第一个字符进行一次比较并返回 false,因为 1!=0。另一方面比较“1234567890”和“1098765432”,会导致执行2个比较操作,因为第一个是相等的,你必须比较第二个才能发现它们不同。这会花费更多的时间,而且它是可以衡量的,即使我们谈论的是远程调用。
如果您使用 N 个不同的字符串进行 N 次攻击,每个字符串都以不同的字符开头,您应该会看到其中一个结果比其余结果多花费几分之一毫秒。这意味着第一个字符相同,因此函数必须花费更多时间来比较第二个字符。冲洗并重复字符串中的每个位置,您可以比蛮力更快地破解 secret 数量级。
防止此类攻击是此类实现的重点。
编辑:正如 Mark Rotteveel 在 comment 中认真指出的那样,此实现仍然容易受到旨在揭示字符串长度的定时攻击。在许多情况下这仍然不是问题(要么你不关心攻击者知道长度,要么你处理标准数据并且任何人都可以知道长度,例如某种已知长度的哈希)
关于使用按位异或的 Java 字符串比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47296524/