恒定时间等于

时间:2016-06-04 18:32:11

标签: java security optimization timing

要防止timing attacks,有时需要一段时间equalsMessageDigest.isEqual没有记录为恒定时间方法和guava HashCode.equals以及其他方法。所有这些都像

boolean areEqual = true;
for (int i = 0; i < this.bytes.length; i++) {
    areEqual &= (this.bytes[i] == that.getBytesInternal()[i]);
}
return areEqual;

    int result = 0;
    for (int i = 0; i < digesta.length; i++) {
        result |= digesta[i] ^ digestb[i];
    }
    return result == 0;

谁说JIT在优化时不会引入短路?

并不难发现,例如,areEqual永远不会再成为现实并打破循环。

我通过根据所有输入位计算值并将其提供给自制的Blackhole来给它a try on CR

4 个答案:

答案 0 :(得分:10)

你无法知道未来

您从根本上无法预测未来的优化者可能会或可能不会使用任何语言。

为了展望未来,最好的机会是操作系统本身提供定时常数测试,这样他们就可以在所有环境中进行适当的测试和使用。

这已经持续了很长时间。例如。 libc中的timingsafe_bcmp()函数首先出现在OpenBSD 4.9中。 (2011年5月发布)。

显然,编程环境需要选择它们和/或提供它们自己保证不会被优化的功能。

检查汇编代码

对优化工具here进行了一些讨论。这是C(和C ++)的思想,但它确实与语言无关,你只能看看当前的优化者可以做什么,而不是未来的优化者可能会做什么。 无论如何,他们理所当然地建议检查汇编代码以了解优化器的功能。

对于那些不一定像C或C ++那样“简单”的Java,考虑到它的本质,但对于特定的安全功能而言,实际上不应该为当前环境做这些工作。

可能避免

您可以尝试避免时间攻击。

E.g:

虽然直观地说,增加随机时间似乎无法做到,但它不起作用:攻击者已经在定时攻击中使用统计分析,你只需要添加更多噪音。

https://security.stackexchange.com/questions/96489/can-i-prevent-timing-attacks-with-random-delays

仍然:如果您的应用程序速度足够慢,这并不意味着您无法实现时间常量。即:等待足够长的时间。例如。您可以等待计时器关闭,然后继续处理比较结果,避免计时攻击。

<强>检测

应该可以使用定时常数比较的实现将定时攻击漏洞检测写入应用程序。

醚:

  • 在初始化期间运行的一些测试
  • 定期进行相同的测试,作为正常操作的一部分。

同样,优化器处理起来会很棘手,因为它可以(有时甚至会)改变事物的执行顺序。但是例如使用程序在其代码中没有的输入(例如外部文件),并运行两次:一次使用普通比较和相同的字符串,一次使用完全不同的字符串(例如xored)然后再使用这些输入但是使用恒定时间比较。你现在有4个时间:正常比较不应该相同,恒定时间比较应该更慢和相同。如果失败:警告应用程序的用户/维护者,生产使用中可能会破坏常量时间。

  • 理论上的选择是自己收集实际时间(记录失败/成功)并自己进行统计分析。但是在实践中执行会很棘手,因为你的测量需要非常精确,因为你不能将它循环几百万次,你只需要测量一次比较并且没有足够精确测量它的分辨率。 ..。

答案 1 :(得分:5)

JIT不仅允许进行此类优化,而且实际上有时

以下是a sample bug我在JMH中发现,短路优化导致基准分数不稳定。尽管使用了(bool == bool1 & bool == bool2)而不是&,JIT已优化&&的评估,即使bool1bool2已声明为volatile

JIT不保证它的优化和不优化。即使您验证它是否按预期工作,未来的JVM版本可能会破坏这些假设。理想情况下,核心JDK库中应该存在用于此类重要安全原语的内在化方法。

您可以尝试通过某些技术避免不希望的优化,例如

  • 涉及volatile字段;
  • 应用增量积累;
  • 产生副作用,例如,写入共享内存等。

但它们也不是100%防弹,因此您必须验证生成的汇编代码并在每次主要Java更新后重新查看它。

答案 2 :(得分:1)

确实,你无法预测优化器会做什么。然而,在这种情况下,您可以合理地执行以下操作:

  1. 计算要比较的值的异或。所用时间仅取决于值的长度。
  2. 计算结果字节的哈希值。使用返回单个整数的哈希函数。
  3. 使用等长的零序列的预先计算的哈希对此哈希进行异或运算。
  4. 我认为这是一个非常安全的选择,哈希函数不会被优化掉。并且整数之间的异或是相同的速度,与结果无关。

答案 3 :(得分:0)

可以阻止优化:

int res = 0;
for (int i = 0; i < this.bytes.length; i++) {
    res |= this.bytes[i] ^ that.getBytesInternal()[i];
}
Logger.getLogger(...).log(FINEST, "{0}", res);
return res == 0;

但是在原始代码上:

使用旧代码,可能应该使用javap进行反汇编,以确保没有进行优化。对于另一个java编译器(如java 9),需要重复它。

JIT推迟了,但是你是对的,优化可能会发生:它需要在循环中进行额外的测试(这本身会减慢每个循环的速度)。

所以你是对的。人们可能只希望在整个测量中效果可以忽略不计。如果只有随机延迟失败,那么其他一些安全防范会有所帮助,不平等,这总是一个很好的绊倒。