这种编码加密哈希的方式安全吗?

信息安全 哈希 沙2
2021-08-30 13:56:07

我正在查看处理文件上传的特定 Web 应用程序的代码。出于某种原因,他们没有使用加密哈希函数(在这种情况下为 SHA-256),而是从中派生一个 ID,并在任何地方使用它来唯一标识文件。

涉及的步骤如下:

  • 计算所需文件的 SHA-256 总和。
  • 每次迭代最多取 3 个字符,并将其视为十六进制字符串,将其转换为等效的 base62 表示法(即0-9a-zA-Z => 0 - 62)。
  • 按该顺序附加这些字符串,并获取“ID”。

例如:

hash (file) = 26ba0a896923d2de4cad532a3f05da725d9cc08d371eaf96905f5bbc1901b56f

26b  -------> 9Z
a0a  -------> Fs
896  -------> zs
923  -------> BJ
d2d  -------> Sp
e4c  -------> X2
ad5  -------> IJ
32a  -------> d4
3f0  -------> gg
5da  -------> oa
725  -------> tv
d9c  -------> Uc
c08  -------> NG
d37  -------> Sz
1ea  -------> 7U
f96  -------> 12m
905  -------> Bf
f5b  -------> 11p
bc1  -------> Mx
901  -------> Bb
b56  -------> KO
f    -------> f

ID = 9ZFszsBJSpX2IJd4ggoatvUcNGSz7U12mBf11pMxBbKOf

对我来说,这似乎根本不是截断散列的安全方法。特别是,在我看来,碰撞的可能性会以这种方式增加。*

上述操作是否会造成问题,或者它们不会干扰 SHA256 的加密强度?

* SHA-2 函数的阻力可能会阻止攻击者利用它。不过,我只关心函数本身的前提。

4个回答

几乎是一个非常好的做法,但它有一点缺陷。

一般来说,散列只是一个数值,你可以用任何你喜欢的基数来表达它。例如,您可以将哈希转换为二进制并将其表示为 base64:

   2   6   b   a  ...
   |   |   |   |
0010011010111010  ...
      |      |
      T      u

但是,您的方法的严重问题是输出的聚类。三个十六进制数字可以转换为一个、两个或三个 base62 数字。没有可靠的方法来决定如何对 base62 值进行聚类。如果您有前导零(即,您将三个十六进制数字转换为三个 base62 数字)和/或使用更大的基数(例如,三个十六进制数字可以映射到两个带有前导零的 base128 数字),您可以避免这个问题。

要查看这方面的实际示例,请考虑 hexf43映射到 base6211103f映射到 base62 11考虑在以下哈希的 base62 形式之间进行区分是不可能的:

f43f43f43f43f43f43f43f43f43f4303f03f03f03f03f03f03f03f03f03f9991
03f03f03f03f03f03f03f03f03f03ff43f43f43f43f43f43f43f43f43f439991
03ff4303ff4303ff4303ff4303ff4303ff4303ff4303ff4303ff4303ff439991

所有这些哈希都转换为

11111111111111111111111111111111111111111111111111CC1

无法知道哪些1s 是三字符组的一部分,哪些是二字符组的一部分。1显然,这是一个极端的例子,但只要一个组的领导不明确,就会出现问题。

但是,三位数和一位数的输出组仅发生在该组可能的 4096 个值中的 314 个,并且这些情况的一小部分只会有歧义。下面来自Gilles的评论估计,截断后的结果将保留 254 位:

据我们所知,SHA-2 哈希的位是独立的。这种截断并不完全剥离位,但它足够接近以至于它也应该是独立的。非唯一性仅涉及每 3 个十六进制数字 lg(12³-62²)≈0.1 位,因此结果应该大致具有 254 位哈希的强度。

两位的损失显然不是最优的,但远非毁灭性的损失。

据我所知,这根本不是截断。每个 12 位部分(3 个 ASCII 十六进制字符)都转换为其等效的 base62 表示,这是一个双射运算。您可以将右侧的值转换回左侧的值。

该操作不会截断该值,而是通过使用更有效的编码来减少其结果长度,就像计算原始哈希字节的 base64 值一样。

“截断”意味着完全删除一部分。在此示例中,如果我截断哈希字符的右半部分,则其余部分将如下所示:26ba0a896923d2de4cad532a3f05da72

所以是的,截断会增加你的碰撞,但这不是这里发生的事情。

如果哈希的十六进制表示的长度是不可接受的,并且想要使用有限的字符集在较短的字符串中唯一地表示哈希,则使用 base-64 而不是 base-64 将允许一个很好的简单映射(即使有替换./具有不同的字符);如果只有 62 个可接受的字符,则可以将数据细分为 64 位块并使用 11 个 base-62 字符来存储每个固定总长度 44,仅比使用 43 个字符的最佳固定长度编码多一个字符(您的编码有时会使用 43 个字符,但有时需要更多,而且不会是唯一的)。在任何具有 64 位无符号整数类型的平台上,以 base-62 编码 64 位都应该相当容易;在不这样做的平台上,可以将 53 位编码为 11 个 base-31 字符,并将剩余的 11 位中的一个添加到每个 base-31 字符以产生 base-32 字符。