【问题标题】:Java HashMap Collision attackJava HashMap 碰撞攻击
【发布时间】:2021-12-31 05:36:41
【问题描述】:

我的目标是用几个字符串(大约 10000 或更多)来攻击 Java 的 HashMap,它们会产生相同的哈希值。我使用了link 上可用的脚本,将其翻译成 Python3(这样我就可以在我的终端上生成字符串)并在我的机器上运行以生成大约 177147 个字符串。当调用 String.hashCode() 方法时,所有这些字符串都会产生相同的哈希值。从link 可以看出,如果对 HashMap 进行随机读写,时间复杂度将是 O(N*N)。如果 N 很大,则需要更多时间(在这种情况下,N 大于 10000)。但令人惊讶的是,它运行时间不到 2 秒。我希望它需要超过 10 秒。以下是我正在使用的脚本。

# Python3 script to generate strings that produce
# same hash with Java's String.hashCode() method
def combs(n,arr,str,arr_rtn):
    if n==1:
        for j in range(3):
            arr_rtn[str + arr[j]] = 0
    else:
        for j in range(3):
            combs(n-1,arr,str+arr[j],arr_rtn)

arr_src = ['at','bU','c6']
str_tmp = ''
arr_rtn = dict()

t = combs(11,arr_src,str_tmp,arr_rtn)

keys = list(arr_rtn.keys())

print(len(keys))
print(*keys, sep = '\n')
// Java code to insert the generated
// strings into HashMap
import java.util.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        HashMap<String, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            String s = sc.next();
            map.put(s, s.hashCode());
            // Should take more time since all strings 
            // hash to same value
        }
        System.out.println(map.size());
        sc.close();
    }
}

我唯一的目标是用产生相同哈希的字符串攻击 HashMap,这样它就需要 20 多秒(至少)来执行。

【问题讨论】:

  • 在较慢的计算机上运行?禁用 JIT?在compareTo 方法中使用sleeps 的特殊键类(或者您必须使用String?)?换句话说,20 秒是任意的。表明您已经到达由于碰撞而导致插入速度变慢的点,而不是达到一些随机持续时间不是更好吗?
  • 请注意,10,000(一万)个元素可能太少了。我刚刚写了一个小测试用例,我用n元素创建了一个LinkedList,然后搜索一个随机元素n次,每次测试加倍n,它只需要20秒或更长时间n = 2^17.
  • 另外,复杂性只告诉您执行时间的增长率。执行算法所需的实际时间将取决于计算机。不要试图让算法花费 20 秒,而是考虑证明复杂性是否足够 n^2。您可以通过不断增加问题规模重复测量算法并分析实验时间增长来做到这一点。

标签: python java hashmap


【解决方案1】:

正如 Tim 所说,从 Java 8 开始,HashMap 将用平衡二叉树替换简单的链表哈希链。发生这种情况:

  • 当链的长度超过硬连线阈值(Java 8 中为 8)时,并且
  • 当数组超过另一个硬连线阈值(Java 8 中为 64)时。

如果键类实现了Comparable&lt;?&gt;(就像String 一样),那么compareTo 方法用于树排序。

这意味着HashMap&lt;String&gt;N 字符串键被设计为冲突的最坏情况将是O(logN) 而不是O(N)

简而言之,针对HashMap 的碰撞攻击在Java 8 或更高版本中将无效。 (但您可以尝试在 Java 7 或更早版本上运行测试攻击。)


如果您需要有关HashMap 的列表与树的工作原理的更多信息,源代码中会对其进行大量注释。找到与您感兴趣的版本相匹配的源代码……并阅读它。


请注意,您作为信息来源引用的问题 (java HashMap collision) 是在 2013 年提出并回答的。Java 8 于 2014 年 3 月发布。

【讨论】:

    【解决方案2】:

    实际上,Java 的HashMap 通过将映射到同一个存储桶的多个值放在一个链表中来处理冲突。因此,在 Java HashMap 中搜索的最差情况下性能是 O(N),其中 N 是映射中的元素数。

    实际上,在 Java 的更新版本中,HashMap 通过将所有相同的桶元素放在树中来处理冲突。这意味着最坏情况下的搜索性能变为O(h),其中h 是树的高度。

    【讨论】:

    • 如果使用链表,随机读写应该需要更多时间,我这里是不是错了?
    • @ChitturiSaiSuman 不,大多数操作最坏的情况应该是O(N),其中N 是碰撞元素的数量。请注意,无论地图中存在多少其他元素/存储桶,找到存储桶本身是 O(1)
    • 是的,找到存储桶本身是 O(1),但要检查给定键映射到什么值,控件必须从头开始迭代链表。因此,一次读取需要 O(N) 时间,因此 N 次随机读取/写入需要 O(N * N)。我错了吗?您还提到最新版本使用Trees,您能详细说明一下吗?
    • @ChitturiSaiSuman 在哈希图中查找键以查找值总是需要恒定的时间。一旦解决了,那么您说的就是正确的,我们将不得不迭代链表。
    • 你能推荐一种方法来让脚本的执行时间超过 20 秒吗? (有点增加碰撞)
    猜你喜欢
    • 2015-07-10
    • 1970-01-01
    • 2011-03-28
    • 2015-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    相关资源
    最近更新 更多