自然溢出哈希 hack 方法
今天不知道在什么地方看到这个东西,感觉挺有意思的,故作文以记之(
当 \(base\) 为偶数时,随便造一个长度 \(>64\) 的字符串,只要它们后 \(64\) 位相同那么俩字符串的哈希值就相同,直接就卡掉了。
当 \(base\) 为奇数时,对于一个只有 \(\text{a}\) 和 \(\text{b}\) 组成的字符串 \(s\),我们定义 \(\bar{s}\) 为将 \(s\) 中所有 \(\text{a}\) 变为 \(\text{b}\),\(\text{b}\) 变为 \(\text{a}\) 的结果,然后我们构造 \(s_1="\text{a}",s_i=s_{i-1}+\overline{s_{i-1}}\),再定义 \(\text{hash}(s)\) 为字符串 \(s\) 的哈希值。那么显然有 \(\text{hash}(s_i)=\text{hash}(s_{i-1})\times base^{2^{i-2}}+\text{hash}(\overline{s_{i-1}}),\text{hash}(\overline{s_i})=\text{hash}(\overline{s_{i-1}})\times base^{2^{i-2}}+\text{hash}(s_{i-1})\),二者相减发现刚好可以表示为 \(\text{hash}(s_i)-\text{hash}(\overline{s_i})\) 的形式。于是我们记一个 \(f_i=\text{hash}(s_i)-\text{hash}(\overline{s_i})\),那么 \(f_i=f_{i-1}\times(base^{2^{i-2}}-1)\),而 \(base^{2^{x}}-1=(base^{2^{x-1}}+1)(base^{2^{x-2}}+1)\cdots(base+1)(base-1)\),由 \(2\nmid base\) 知这 \(x\) 个括号里的东西全是偶数,故 \(2^{x+1}\mid base^{2^x}-1\),于是 \(2^{i(i-1)/2}\mid f_i\),故对于 \(i\ge 12\),\(\text{hash}(s_i)=\text{hash}(\overline{s_i})\),构造一个长度 \(4096\) 的字符串即可叉掉。
这个故事告诉我们,以后千万不要写自然溢出哈希了