java 恒定时间等于

pw136qt2  于 2023-01-29  发布在  Java
关注(0)|答案(5)|浏览(159)

为了防止timing attacks,有时需要一个恒定时间equals。有没有记录的MessageDigest.isEqual是一个恒定时间的方法和GuavaHashCode.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

fumotvh3

fumotvh31#

"你无法预知未来"
根本上,您无法预测未来的优化器在任何语言中可能做什么或不可能做什么。
展望未来,最好的办法是操作系统本身提供定时常数测试,这样它们就可以在所有环境中正确地测试和使用。
这已经进行了相当长的一段时间,例如libc中的timingsafe_bcmp()函数最早出现在OpenBSD 4.9中(2011年5月发布)。
显然,编程环境需要拾取这些和/或提供它们自己的函数,它们保证不会被优化掉。

    • 检查装配代码**

这里有一些关于优化器的讨论。这是C(和C++)的思想,但它实际上是语言独立的,你只能看看当前的优化器能做什么,而不是未来的优化器可能会做什么。无论如何,他们正确地建议检查汇编代码,以了解你的优化器做什么。
对于java来说,考虑到它的本质,这不一定像C或C++那样"容易",但对于特定的安全功能来说,在当前环境中实际完成这项工作应该不是不可能的。

    • 有可能回避**

你可以试着避开时机攻击。
例如:

尽管直观上看,添加随机时间似乎是可行的,但它不会起作用:攻击者已经在计时攻击中使用了统计分析,您只不过添加了一些噪声。
https://security.stackexchange.com/questions/96489/can-i-prevent-timing-attacks-with-random-delays
不过,这并不意味着如果您的应用程序足够慢,您就不能实现时间常数。等待足够长的时间。eidogg.你可以等待计时器关闭,然后才继续处理比较的结果,以避免计时攻击。

    • 检测**

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

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

优化器也是很难处理的,因为它甚至可以(有时候会)改变执行顺序,但是,例如,使用程序代码中没有的输入(例如外部文件),并运行两次:一次是正常的比较和相同的字符串,一次是完全不同的字符串(例如异或),然后再一次是这些输入,但进行恒定时间的比较。正常比较不应相同,恒定时间比较应较慢且相同。如果失败:警告应用程序的用户/维护人员,恒定时间填充可能在生产使用中损坏。

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

ljo96ir52#

JIT不仅被允许做这样的优化,而且有时候它确实做了这样的优化。
这是我在JMH中找到的a sample bug,其中的短路优化导致基准测试分数不稳定。JIT优化了(bool == bool1 & bool == bool2)的评估,尽管使用的是&而不是&&,即使bool1bool2声明为volatile
JIT并不能保证它优化了什么,没有优化什么。即使你验证了它能像你想要的那样工作,未来的JVM版本可能会打破这些假设。理想情况下,核心JDK库中应该有内部化的方法来处理这样重要的安全原语。
您可以尝试通过某些技术来避免不需要的优化,例如:

  • 涉及易变领域;
  • 采用增量累加;
  • 产生副作用,例如写入共享内存等。

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

xwmevbvl

xwmevbvl3#

实际上,你无法预测优化器会做什么。然而,在这种情况下,你可以合理地做以下事情:
1.计算被比较的值的异或。所花费的时间只取决于值的长度。
1.计算结果字节的哈希值。使用哈希函数返回单个整数。
1.将此哈希与长度相等的零序列的 * 预计算 * 哈希进行异或运算。
我认为哈希函数是不会被优化掉的,这是一个相当安全的赌注,而且整数之间的异或运算无论结果如何,速度都是一样的。

yzckvree

yzckvree4#

可以这样防止优化:

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编译器(如java9),可能需要重复这一步。
JIT开始晚了,但是你是对的,优化可能发生:则在循环中需要额外的测试(其本身减慢了每个周期)。
所以你是对的。人们可能只希望在整个测量过程中这种影响是可以忽略的。还有一些其他的安全措施会有所帮助,如果只是一个失败的随机延迟,不平等,这总是一个很好的绊脚石。

bzzcjhmw

bzzcjhmw5#

您可以并且应该使用java.security.MessageDigest.isEqual(byte[], byte[]),它(至少在以后的Java版本中)被记录用于此目的:

* @implNote
* All bytes in {@code digesta} are examined to determine equality.
* The calculation time depends only on the length of {@code digesta}.
* It does not depend on the length of {@code digestb} or the contents
* of {@code digesta} and {@code digestb}.

相关问题