先贴java String类源码:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
这是字符串比较函数的常见实现,不受定时攻击的影响。
简而言之,这个想法是每次比较字符串时比较所有字符,即使您发现其中任何一个字符都不相等。在“标准”实现中,您只需中断第一个差异并返回false。
这是不安全的,因为它会泄露有关比较字符串的信息。具体来说,如果左边的字符串是你想保留的秘密(例如密码),而右边的字符串是用户提供的,那么不安全的方法可以让黑客通过反复尝试不同的字符串和测量响应时间,相对轻松地发现你的密码。两个字符串中相同的字符越多,“unsecure”函数就越需要比较它们。
例如,使用标准方法比较“1234567890”和“0987654321”将导致仅对第一个字符进行一次比较并返回false,因为1=0另一方面,比较“1234567890”和“1098765432”,将导致执行两个比较操作,因为第一个操作相等,所以必须比较第二个操作才能发现它们不同。这将需要更多的时间,而且是可以测量的,即使在我们谈论远程通话时也是如此。
如果您使用N个不同的字符串进行N次攻击,每个字符串都以不同的字符开头,您应该会看到其中一个结果比其他结果多花费了几分之一毫秒。这意味着第一个字符是相同的,因此函数需要花费更多的时间来比较第二个字符。冲洗和重复在字符串中的每个位置,你可以破解的秘密数量级快于暴力。
如何防止此类攻击呢?直接上代码
private static boolean SecureEquals(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; }
每个位置上做异或运算 再与上次结果做或运算。这样 每次返回时间都是一样的。