恒定时间等于

maa*_*nus 13 java security optimization timing

为了防止定时攻击,equals有时需要一个恒定的时间.还有的MessageDigest.isEqual没有记录是恒定的时间方法和番石榴HashCode.equals等.他们都做了类似的事情

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

要么

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

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

发现例如并不难,这areEqual将永远不会成为现实并打破循环.


我通过计算取决于所有输入位的值并将其馈送到自制来尝试CRBlackhole.

小智 11

你不可能知道未来

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

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

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

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

检查汇编代码

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

对于那些不一定像C或C++一样"容易"的Java,考虑到它的本质,但对于特定的安全功能而言,实际上不应该为当前环境做这些努力.

避免可能是可能的

您可以尝试避免定时攻击.

例如:

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

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

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

发现

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

醚:

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

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

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


apa*_*gin 5

JIT不仅允许进行这样的优化,而且实际上有时会这样做.

这是我在JMH中发现的一个示例错误,其中短路优化导致不稳定的基准分数.JIT优化了的评价(bool == bool1 & bool == bool2),尽管&使用而不是&&,即使bool1bool2被宣布volatile.

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

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

  • 涉及易变场;
  • 应用增量积累;
  • 产生副作用,例如,写入共享内存等.

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


Joo*_*gen 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;
Run Code Online (Sandbox Code Playgroud)

但在原始代码上:

对于旧代码,也许应该用 javap 进行反汇编,看看是否没有进行优化。对于另一个 java 编译器(如 java 9),需要重复这一过程。

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

所以你是对的。人们可能只希望这种影响在整个测量过程中可以忽略不计。其他一些安全措施也有帮助,即使只是随机延迟失败,不平等,这总是一个很好的绊脚石。