我试过搜索但什么也没找到(如果它们已经是东西,不知道这些会被称为什么,所以搜索有点困难),所以如果这是愚蠢的或者已经在某个地方回答了,请原谅我。为了争论,当我说我正在散列某些东西时,可以说我正在使用 bcrypt 或具有那种声誉/质量的东西。
首先,您的散列算法是否有理由不能随密码或其中间散列而变化?
public static byte[] myHash(byte[] input, byte[] saltA, byte[] saltB) {
return input[0] % 2 == 0
? bcrypt(bcrypt(input, saltA), saltB)
: bcrypt(bcrypt(input, saltB), saltA);
}
我觉得这不会使用太多 CPU - 它只是 bcrypt 的两次迭代,我在其他安全性讨论中建议 10 多次迭代 - 但是如果你知道盐和哈希,bcrypt 已经发现它是完全可逆的, 现在需要取消哈希两次 - 一次使用saltA
, 然后使用saltB
, 反之亦然, 为您提供两个候选密码, 其中一个是具有 50% 误报率的诱饵 (也就是说, 它重新散列到正确的散列, 因为它的第一位是正确的偶数或奇数),需要启发式或人眼才能正确识别真实的,因此您至少需要双倍的计算资源,并且可能需要人工干预。但我们可以做得更好:
public static byte[] myBetterHash(byte[] input, byte[] saltA, byte[] saltB) {
byte[] curr = input;
for (int i = 0; i < 5; i++) {
switch(input[i] % 3) {
case 0: curr = bcrypt(bcrypt(bcrypt(curr, input), saltA), saltB); break;
case 1: curr = bcrypt(bcrypt(bcrypt(curr, saltB), input), saltA); break;
case 2: curr = bcrypt(bcrypt(bcrypt(curr, saltA), saltB), input); break;
}
}
return input;
}
现在,在 5 次迭代中,每次迭代有 3 次 unhashes,产生 243 个候选密码,可能还有几十个误报要消除,但即使没有,也必须做 243 倍于你刚刚完成的 unhashing 工作。此外,在后续哈希中再次将输入作为盐包含在内,使得实际上无法进行反哈希,而且它需要攻击者保留一点额外的内存。也就是说,我的最后一个想法如下:
public static byte[] myBestHash(byte[] input, byte[] saltA, byte[] saltB) {
byte[] curr = input;
byte[][] arr = new byte[16][]
for (int i = 0; i < 16; i++) {
arr[i] = curr;
switch(curr[0] % 4) {
case 0: curr = bcrypt(curr, saltA); break;
case 1: curr = bcrypt(curr, saltB); break;
case 2: curr = bcrypt(curr, arr[input[i] % i]); break;
case 3: curr = bcrypt(bcrypt(curr, saltA), saltB); break;
}
}
return input;
}
现在攻击者必须处理大量潜在的反散列(3^16 = 超过 400 万),每一个都必须使用上述内存密集型进行验证(它持有 16 个中间散列,并且无法优化它) )。
其次,我觉得最后一个示例的内存密集度与分支盐配对,甚至可能其中一个分支调用 bcrypt 两次而不是一次这一事实,在某种组合下,通过制作手头的大头钉不适合他们或使过程浪费比正常情况更多的 I/O。如果不出意外,将这种方法扩展到 16 次以上迭代将继续增加 RAM 使用量,使其更难并行化。想象一下,如果使用了 256 次迭代,并且必须为攻击中泄露的每个散列保留 1024 个中间散列的空间——如果中间散列本身是 1024 位(= 128 字节),那么每个散列浪费了 32kB 的内存。蛮力攻击的迭代,这并不多,
那么,我是不是在做某事,或者这完全是愚蠢的?