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