使用这种自定义散列函数,我是否有任何明显的安全漏洞?

时间:2012-10-13 06:07:21

标签: php security function md5

<?php
function md7($text, $len)
{
  if($text)
  {
   $split = str_split(md5($text).md5(strlen($text)).md5($len), 5);
    foreach($split as $key=>$value)
    {
      $md5 = $md5.md5($value);
    }
    $split2 = str_split($md5);
    foreach($split2 as $kl=>$vl)
    {
      if($kl < $len)
      {
        $digest = $digest.$split2[$kl];
      }   
    }
    return $digest;
  }
} // end md7 function
?>

我创建了这个函数来使用md5和一个可变长度哈希,我相信这减少了碰撞的机会,我已经测试了MD5碰撞的例子,并且没有为这个函数创建一个碰撞,这个功能我相信不会受到彩虹表攻击的影响。

3 个答案:

答案 0 :(得分:4)

以下是一些明显的安全漏洞:

if($text)

"0"""都会返回相同的哈希值。

foreach($split as $key=>$value)
{
  $md5 = $md5.md5($value);
}

您正在散列第一个哈希的每个字母。使用$len <= 32,这将使得对于md5($value)的第一个字母相同的所有字符串具有完全相同的哈希值,从而将熵从128位减少到4.这是一个完整的$ len = 32的所有16个哈希值的列表:

8f14e45fceea167a5a36dedd4bea2543
92eb5ffee6ae2fec3ad71c777531578f
a87ff679a2f3e71d9181a67b7542122c
e4da3b7fbbce2345d7772b0674a318d5
c81e728d9d4c2f636f067f89cc14862c
8277e0910d750195b448797616e091ad
0cc175b9c0f1b6a831c399e269772661
45c48cce2e2d7fbdea1afc51c7c6ad26
4a8a08f09d37b73795649038408b5f33
e1671797c52e15f763380b45e841ec32
eccbc87e4b5ce2fe28308fd9f2a7baf3
c4ca4238a0b923820dcc509a6f75849b
8fa14cdd754f91cc6554c9e71929cce7
c9f0f895fb98ab9159f51fd0297e236d
1679091c5a880faf6fb5e6087eb1b2dc
cfcd208495d565ef66e7dff9f98764da

请注意,选择$len > 32不会缓解此问题。你仍然只会使用原始哈希的第二个字母,它会给你4位多的熵(现在最多8位),等于256个不同的哈希值。

我很确定你需要32 ^ 32的长度来匹配原始md5的熵。这是一个非常大的数字。


如果您想要安全性,请使用定义良好且经过良好测试的散列函数。 PHP在sha1函数中具有hash函数以及许多其他函数。

哈希函数通常由加密社区创建和审核。它们比你提出的几乎任何简单的hack要好得多,所以不要实现自己的散列函数,而是使用其中一个可用的散列函数。

答案 1 :(得分:3)

除了你的函数没有为"""0"返回任何内容之外,最终的哈希值只包含十六进制字符0的{​​MD5哈希值 - {{1} }和9 - a,它们是:

f

如果我们对string(32) "cfcd208495d565ef66e7dff9f98764da" string(32) "c4ca4238a0b923820dcc509a6f75849b" string(32) "c81e728d9d4c2f636f067f89cc14862c" string(32) "eccbc87e4b5ce2fe28308fd9f2a7baf3" string(32) "a87ff679a2f3e71d9181a67b7542122c" string(32) "e4da3b7fbbce2345d7772b0674a318d5" string(32) "1679091c5a880faf6fb5e6087eb1b2dc" string(32) "8f14e45fceea167a5a36dedd4bea2543" string(32) "c9f0f895fb98ab9159f51fd0297e236d" string(32) "45c48cce2e2d7fbdea1afc51c7c6ad26" string(32) "0cc175b9c0f1b6a831c399e269772661" string(32) "92eb5ffee6ae2fec3ad71c777531578f" string(32) "4a8a08f09d37b73795649038408b5f33" string(32) "8277e0910d750195b448797616e091ad" string(32) "e1671797c52e15f763380b45e841ec32" string(32) "8fa14cdd754f91cc6554c9e71929cce7" 使用相同的内容:

md7($c, 32)

再次,NULL string(32) "4a8a08f09d37b73795649038408b5f33" string(32) "4a8a08f09d37b73795649038408b5f33" string(32) "e1671797c52e15f763380b45e841ec32" string(32) "0cc175b9c0f1b6a831c399e269772661" string(32) "e1671797c52e15f763380b45e841ec32" string(32) "c4ca4238a0b923820dcc509a6f75849b" string(32) "c9f0f895fb98ab9159f51fd0297e236d" string(32) "4a8a08f09d37b73795649038408b5f33" string(32) "a87ff679a2f3e71d9181a67b7542122c" string(32) "cfcd208495d565ef66e7dff9f98764da" string(32) "45c48cce2e2d7fbdea1afc51c7c6ad26" string(32) "a87ff679a2f3e71d9181a67b7542122c" string(32) "c9f0f895fb98ab9159f51fd0297e236d" string(32) "e1671797c52e15f763380b45e841ec32" string(32) "c9f0f895fb98ab9159f51fd0297e236d" 返回0。但更有趣的是{NULL12},{835}和{的字符7c}会产生相同的哈希值。这是因为它们的MD5哈希值确实以相同的八位字节开始。当你使用这些哈希来构建你的最终MD7哈希时,它们产生的哈希的前32个十六进制字符也是相同的。

因此,对于≤32的长度,只有16个可能的MD7哈希,对于≤64,有16个 2 MD7哈希,依此类推。最大长度为3·32·32 = 3072,可能的MD7哈希值为16 3·32

但最终的3072个字符长MD7哈希没有16 ^ 192熵。因为最后1024个字符是从已知的MD7哈希的长度派生的,所以只剩下2048个未知字符。

由于可以反转散列循环,还可以获得文本的初始MD5及其长度。

以下是一个例子:

f

原始文本长度的MD5也可以反转。所以最后剩下的信息是MD5和文本长度。获得的安全性不高。事实上,文本长度的知识确实揭示了其他信息。

答案 2 :(得分:1)

这里有一个问题可能值得考虑,但我无法轻易分析md5哈希值。

让我们分两部分考虑,前半部分取输入,散列它的长度和返回的长度。我不确定数组与字符串连接的效果是什么,但我会假设它只是为每个实例复制。

然后,您遍历每个并构建一个新值:$md5 = $md5.md5($value)。鉴于你正在返回一个最多$ len的字符串,这似乎是最大的弱点,因为你从字符串返回前$ len个字节。

一些效果:$ md5主要取决于输入的开头。尝试像

这样的东西
md7("aaaaaaaaaaaaaaaaaaaaaaaa", 16)
md7("aaaaaaaaaaaaaaaaaaaaaaab", 16)

看看你是否得到了类似的结果。我没有试过这个,但我的预感是你没有像你想要的那样正确混合整个输入。