【问题标题】:shorter php cipher than md5?比 md5 更短的 php 密码?
【发布时间】:2010-09-21 19:27:16
【问题描述】:

出于各种愚蠢的原因,我们发布到外部服务器的给定表单变量的最大长度为 12 个字符。

我想用 md5 来掩盖这个值,但显然用 12 个字符是行不通的。是否有一个已经生成的 PHP 函数的密码会导致 12 个字符或更少?

密码的安全性和完整性在这里并不重要。我最后的办法是编写一个函数,将每个字母向上或向下移动一个 ascii 值 x。因此,我们的目标不是让密码学专家掩盖它,而是不要以纯文本形式发布它,这样非技术人员看到它就不会知道它是什么。

感谢您的建议。

【问题讨论】:

  • 我很想建议在某处拨打str_rot13() 电话。更严重的是,如果您需要检索解密版本,则无论如何都不能使用md5(),因为这是一个哈希函数,这使得它几乎本质上是一种单向加密。
  • 请不要使用凯撒密码。有很多小/弱加密/解密算法可供选择,可以适应您的 12 char 模型。凯撒密码太容易破解了。最终你会找到一个模式,即使是指关节拖拉机也能看得一清二楚。
  • 乔尔,你会推荐哪一个?
  • 我可以补充一点,如果这是一个隐藏的表单域,非技术人员可能不会看到它。唯一能看到的将是好奇的人试图弄清楚它。您可以通过进行一些字符转换(例如凯撒密码)来阻止或减慢它们的速度。如果您确实需要根本不显示该值,则可以将表单发送回您的服务器(您可以将变量从普通视图中隐藏起来),然后您的服务器可以将任何结果发送到远程服务器。我不知道这是否适合您的用例。
  • 将您的结果从md5($data) 传递到Alphabet::convert($hash, Alphabet::HEX, Alphabet::ALPHANUMERIC),然后传递到substr($result, 0, 12),以保留最多的信息。

标签: php variables encryption cryptography md5


【解决方案1】:

也许这将帮助您生成一个 12 字符的字符串,您可以将其传递到 URL 中,而不会增加冲突的风险

substr(base_convert(md5($string), 16,32), 0, 12);

【讨论】:

  • 这应该是公认的答案。它是更紧凑的表示形式的完整 MD5 哈希。
  • 这不会保留完整的 MD5 哈希...根据 PHP 文档,“警告:由于与内部“双精度”或“浮点”相关的属性,base_convert() 可能会丢失大量数字的精度“使用的类型。”至少在 32 位系统上,将 32 位十六进制转换为 base-32 会导致最后 16 位设置为 0。试试吧: print base_convert(md5('foo'), 16, 32);结果为 5cnkcdmj62v000000000000000。
  • @mikeker 正如您提到的php.net/manual/es/function.base-convert.php#109660,这可能有助于避免丢失精度
  • 为了完整起见,您需要在服务器中安装php7.0-bcmath才能使用@Gabriel提供的功能
【解决方案2】:

这是对this answer 的补充。

答案建议从 md5 的 32 个字符表示中取前 12 个字符。因此 20 个字符的信息将丢失 - 这将导致 方式 更多可能的冲突。

您可以通过采用 16 个字符表示(原始形式)的前 12 个字符来减少信息丢失:

substr(md5($string, true), 0, 12);

这将保留 75% 的数据,而使用 32 char 形式仅保留 37.5% 的数据。

【讨论】:

  • 这会产生 12 个 bytes,不一定是 12 个 characters,而且将它与非二进制安全函数一起使用也是有风险的,不幸的是仍然很常见,因为它们会在第一个 \0 字符处截断。
  • md5( $string, true ) 不返回不可见字符吗?我认为这会导致 HTML 出现问题 - 哈希可能会被浏览器 urlencoded 并改变长度......
【解决方案3】:

试试 crc32() 可能吗?

【讨论】:

    【解决方案4】:

    您可以使用更大的字母表并使哈希值更短,但仍可恢复为原始值。

    我实现了它 here - 例如,哈希 ee45187ab28b4814cf03b2b4224eb974 变为 7fBKxltZiQd7TFsUkOp26w - 它从 32 个字符变为 22 个字符。如果您使用更大的 alpahabet,它可能会变得更少。如果你使用 unicode,你甚至可以用 emoji 对 hash 进行编码...

    【讨论】:

    • 你有PHP版本吗?
    【解决方案5】:

    所有答案都建议丢失一些数据(更高的碰撞可能性),但看起来使用基本转换是一种更好的方法: 例如就像这里描述的http://proger.i-forge.net/Short_MD5/OMF

    您还可以生成任何随机字符串并将其插入数据库,在保存之前检查是否不存在。这将允许您拥有较短的哈希值,并确保没有冲突。

    【讨论】:

      【解决方案6】:

      我必须提出这个建议,因为我必须假设您可以控制将您的加密值发送到的脚本......

      我还必须假设您可以创建许多表单字段,但每个字段的长度不能超过 12 个字符。

      如果是这样,你能不能简单地创建多个表单域并将 md5 字符串分散到多个隐藏域中?

      您可以将 md5 字符串分成 8 个块,并在隐藏的表单字段中提交每个块,然后在另一端将它们连接在一起。

      只是一个想法......

      【讨论】:

        【解决方案7】:

        如果您只需要哈希,您仍然可以使用 md5 哈希中的前 12 个字符。

        substr(md5($yourString), 0, 12);
        

        【讨论】:

        • 我输入的也是一样的!
        • 我应该澄清一下,我们仍然需要对其进行解码。可能存在一个 md5 的前 12 个字符与另一个相同的情况。
        • @tiredofcoding:MD5 与任何其他哈希一样,是一种单向函数:您无法对其进行解码。你想要的是一个密码,而不是一个哈希。
        • 我知道。我的意思是,我们将维护我们用 md5 编码的每条数据的值。所以我们会知道值 x = md5 值 y。
        • "value x = md5 value y" 和 first12LettersFrom(value x) = first12LettersFrom(md5 value y)。我真的看不出这里有什么问题。
        【解决方案8】:

        这可能对 OP 没有用,因为他们正在寻找 2 路函数,但可能有助于寻找比 md5 更短的散列的人。这是我为满足我的需求而提出的(感谢https://rolandeckert.com/notes/md5 突出显示了base64_encode 函数)。将 md5 哈希编码为 base(64) 并删除任何不需要的 base(64) 字符。我正在删除元音 + 和 / 从而将有效基数从 64 减少到 52。

        请注意,如果您在 c 个字符之后截断 base(b) 编码的哈希,它将允许 b ^ c 唯一的哈希。这是否足以避免碰撞?这取决于您要散列的项目数 (k)。碰撞的概率大约是 (k * k) / (b ^ c) / 2,所以如果你使用下面的函数来散列 k = 100 万个项目,基数 b = 52 编码在 c = 12 个字符后截断,碰撞概率小于 7.5 亿分之一。与在 c = 12 个字符后截断十六进制编码 (b = 16) 散列进行比较。碰撞的概率大约是 500 分之一!对截断十六进制编码的哈希说不。 :)

        我会冒险说下面的函数(长度为 12)对于 1000 万件物品来说是相当安全的(

        // convert md5 to base64, remove undesirable characters and truncate to $length
        function tinymd5($str, $length) { // $length 20-22 not advised unless $remove = '';
            // remove vowels to prevent undesirable words and + / which may be problematic
            $remove = array('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U', '+', '/');
            $salt = $str;
            do { // re-salt and loop if rebase removes too many characters
                $salt = $base64 = base64_encode(md5($salt, TRUE));
                $rebase = substr(str_replace($remove, '', $base64), 0, $length);
            } while ($length < 20 && substr($rebase, -1) == '=');
            return str_pad($rebase, min($length, 22), '='); // 22 is max possible length
        }
        
        $str = 'Lorem ipsum dolor sit amet 557726776';
        echo '<br />' . md5($str);         // 565a0bf7e0ba474fdaaec57b82e6504a
        $x = md5($str, TRUE);
        echo '<br />' . base64_encode($x); // VloL9+C6R0/arsV7guZQSg==
        echo '<br />' . tinymd5($str, 12); // VlL9C6R0rsV7
        echo '<br />' . tinymd5($str, 17); // VlL9C6R0rsV7gZQSg
        $x = md5(base64_encode($x), TRUE); // re-salt triggered < 20
        echo '<br />' . base64_encode($x); // fmkPW/OQLqp7PTex0nK3NQ==
        echo '<br />' . tinymd5($str, 18); // fmkPWQLqp7PTx0nK3N
        echo '<br />' . tinymd5($str, 19); // fmkPWQLqp7PTx0nK3NQ
        echo '<br />' . tinymd5($str, 20); // fmkPWQLqp7PTx0nK3NQ=
        echo '<br />' . tinymd5($str, 22); // fmkPWQLqp7PTx0nK3NQ===
        

        【讨论】:

        • 感谢您的代码。有办法解密吗?
        【解决方案9】:

        $hashlen = 4;
        $cxstrong = 真;
        $sslk = openssl_random_pseudo_bytes($hashlen, $cxtrong);
        $rand = bin2hex($sslk);

        回声 $rand;

        你可以通过改变变量$hashlen的值来改变哈希长度(2的倍数)

        【讨论】:

          【解决方案10】:

          我想出了以 90 为底的方法,用于将 md5 减少到 20 个多字节字符(我测试它可以正确地放入 mysql 的 varchar(20) 列中)。不幸的是,这实际上使字符串可能比 php 的 md5 中的 32 个字节还要大,唯一的优点是它们可以存储在 varchar(20) 列中。当然,如果您担心存储问题,您可以将字母表替换为单字节字母...

          如果您的想法是在 mysql 之类的东西和其他类型的处理中使用这个简化的哈希作为查找键,则需要牢记一些重要的规则:

          1. 默认情况下,MySQL 不会在典型的 where 子句中区分大写和小写,该子句从可能的目标字母表中取出大量字符。这不仅包括英文字符,还包括几乎所有其他语言的字符。

          2. 重要的是,您的哈希可以透明地大写和小写,因为许多系统将这些键大写,因此为了在这个意义上保持它与 md5 一致,您应该在使用可区分大小写的字符时只使用小写。

          这是我使用的字母表(我精心挑选了每个字符以使哈希值尽可能好):

          define('NICIESTCHARS', [
              "0","1","2","3","4","5","6","7","8","9",
              "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
              "¢","£","¥","§","¶","ø","œ","ƒ","α","δ","ε","η","θ","ι","λ","μ","ν","π","σ","τ","φ","ψ","ω","ћ","џ","ѓ","ѝ","й","ќ","ў","ф","э","ѣ","ѷ","ѻ","ѿ","ҁ","∂","∆","∑","√","∫",
              "!","#","$","%","&","*","+","=","@","~","¤","±"
          ]);
          

          这是 PHP 中的代码(我想它不是最好的代码,但可以完成工作)。请记住,它仅适用于长度为 8 的倍数的六进制 (0-F) 字符串,例如 php 中的 md5,即 32 个 0-f 字节:

          function mbStringToArray ($string) {
              $strlen = mb_strlen($string);
              while ($strlen) {
                  $array[] = mb_substr($string,0,1,"UTF-8");
                  $string = mb_substr($string,1,$strlen,"UTF-8");
                  $strlen = mb_strlen($string);
              }
              return $array;
          } 
          
          class Base90{ 
              static function toHex5($s){
                  // Converts a base 90 number with a multiple of 5 digits to hex (compatible with "hexdec").
                  $chars = preg_split('//u', $s, null, PREG_SPLIT_NO_EMPTY);
                  $map = array_flip(NICIESTCHARS);
                  $rt = '';
                  $part = [];
                  $b90part = '';
                  foreach($chars as $c){
                      $b90part .= $c;
                      $part[] = $map[$c];
                      if(count($part) == 5){
                          $int = base90toInt($part);
                          $rt .= str_pad(dechex($int), 8, "0", STR_PAD_LEFT);
                          $part = [];
                          $b90part = '';
                      }
                  }
                  return $rt;
              }
              
              static function fromHex8($m){
                  // Converts an hexadecimal number compatible with "hexdec" to base 90
                  $parts = [];
                  $part = '';
                  foreach(str_split($m) as $i => $c){
                      $part.= $c;
                      if(strlen($part) === 8){
                          $parts[] = intToBase90(hexdec($part));
                          $part = '';
                      }
                  }
                  return implode('', $parts);
              }
          }
          
          
          
          function intToBase90($int){
              $residue = $int;
              $result = [];
              while($residue){
                  $digit = $residue % 90;
                  $residue -= $digit;
                  $residue = $residue / 90;
                  array_unshift($result,  NICIESTCHARS[$digit]);
              }
              $result = implode('', $result);
              return $result;
          }
          
          function base90toInt($digits){
              $weight = 1;
              $rt = 0;
              while(count($digits)){
                  $rt += array_pop($digits)*$weight;
                  $weight *= 90;
              }
              return $rt;
          }
          

          【讨论】:

            猜你喜欢
            • 2021-04-10
            • 1970-01-01
            • 2015-02-09
            • 2012-11-01
            • 2012-02-05
            • 1970-01-01
            • 2014-09-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多