【问题标题】:Reduce MD5 - Using a different base减少 MD5 - 使用不同的基数
【发布时间】:2018-11-05 12:53:21
【问题描述】:

我的客户生成的促销优惠券代码不过是 32 个字符 MD5 哈希值。

我的工作是将 MD5 字符串从 32 个字符减少到少于 10 个字符,以便可以从减少的字符串重新创建哈希。

减少很重要,因为用户更容易重现减少的哈希。

例如:719bedacf2e560b27f39d80accc67ffd => ZjKa1Gh(数学上不正确)

我遇到了这个:How to reduce hash value's length?

这表明:使用不同的基础

我不知道如何在 PHP 中执行此操作,我们可以将字符串解码为其 ASCII 并重新编码吗?

在这种情况下我可以使用 PHP 中的任何内置函数吗?

使用https://packagist.org/packages/aza/math更新

$original = '719bedacf2e560b27f39d80accc67ffd';
$long1 = NumeralSystem::convert($original, 16, 10);
$short = NumeralSystem::convertTo($long1, 62);
$long2 = NumeralSystem::convertFrom($short, 62);
$recovered = NumeralSystem::convert($long2, 10, 16);

var_dump($long1);
var_dump($short);
var_dump($long2);
var_dump($recovered);

// output
string(39) "151012390170261082849236619706853916669"
string(22) "3SNOKWefotgnnCmWnYkTOf"
string(39) "151012390170261082849236619706853916669"
string(32) "719bedacf2e560b27f39d80accc67ffd"

似乎我可以从 32 个字符 MD5 中达到的最低值是 22 个字符。我仍在寻找可以将其进一步减少到 10 个字符的方法。

更新:使用 MD5 的前半部分

$original = '719bedacf2e560b';
$coupon = NumeralSystem::convert($original, 16, 62);
$recovered = NumeralSystem::convert($coupon, 62, 16);

var_dump($coupon);
var_dump($recovered);

// output
string(10) "bnMR3RjZil"
string(15) "719bedacf2e560b"

如果用户提供bnMR3RjZil,我可以使用它重新创建719bedacf2e560b,然后执行MySQL LIKE 搜索以获得完整的MD5。如果它返回一行,我就可以继续进行促销活动。

【问题讨论】:

  • 目前还不清楚您到底想做什么。没有办法将给定的 md5 哈希“减少到它的 ASCII”,这应该是什么意思?哈希已经是 ASCII 编码。
  • 我的意思是询问字符串是否可以在 PHP 中简化为它的 ASCII,从某种意义上说,如果有 A,我会得到 065 作为输出。如果我能得到065,那么我可以用不同的方式存储它。
  • 啊,你的意思是字符代码。你正在寻找php.net/manual/en/function.ord.php,但我怀疑这会让你前进。

标签: php hash base


【解决方案1】:

加密哈希实际上是一个位序列,但它可以解释为一个数字。因此,理论上您可以使用良好的旧base_convert() 来表示基数很大。不幸的是,这个函数只适用于基数 36,并且仅限于实际数字(即适合 PHP_INT_MAX 的短整数)——否则会发生数据丢失。

这就是第三方库可以提供帮助的地方。唯一的问题是它们往往很难找到,因为它们通常针对非常具体的用例(比特币、ID 混淆等)。

我发现例如aza/math,这可能有点矫枉过正,但应该可以完成工作。我没有机会测试它,但它应该是这样的:

$original = '719bedacf2e560b27f39d80accc67ffd';
$short = NumeralSystem::convert($original, 16, 62);
$recovered = NumeralSystem::convert($short, 62, 16);

【讨论】:

    【解决方案2】:

    使用不同碱基的方法可以如下进行。请注意,下面的代码只是为了说明该方法,为了有效地实现它,需要直接使用二进制表示。

    这个想法是将输入字符串解释为 128 位序列。现在,如果您指定您的新字母表(新基本系统的字符)为A-Za-z0-9+-,则您有 64 个字符,这意味着您需要 6 位来对每个字符进行编码。因此,您可以先将输入字符串转换为二进制表示,将此表示拆分为 6 位的块,并在指定字符集 A-Za-z0-9+- 内表示每个块:

    <?php
    
    $s = "719bedacf2e560b27f39d80accc67ffd";
    
    function conv($s){
      $ret = base_convert($s, 16, 2);
      return str_repeat("0", 8 - strlen($ret)) . $ret;
    }
    
    $binary_repr = implode(array_map(conv, str_split($s, 2)), '');
    
    $items = str_split($binary_repr, 6);
    
    function item2char($str){
      $code = base_convert($str, 2, 10);
      $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-';
      return $alphabet[$code];
    }
    
    $result = implode(array_map(item2char, $items), '');
    echo $result;
    
    ?>
    

    正如 cmets 中所指出的,这基本上是背后的想法:

    <?php
    $s = "719bedacf2e560b27f39d80accc67ffd";
    
    echo base64_encode(hex2bin($s));
    //cZvtrPLlYLJ/OdgKzMZ//Q==
    
    echo bin2hex(base64_decode("cZvtrPLlYLJ/OdgKzMZ//Q=="));
    //719bedacf2e560b27f39d80accc67ffd
    
    ?>
    

    【讨论】:

    • 感谢您,我认为这是一个很好的开始,我可以根据自己的具体需求进行改进。
    • 您所描述的已经在 PHP 中有一个名称和一个实现——它是 Base64。 base64_encode(md5($input, true)).
    • @duskwuff 是的,我想说明方法,我修改了答案
    【解决方案3】:

    我的工作是将 MD5 字符串从 32 个字符减少到少于 10 个字符,以便可以从减少的字符串重新创建哈希。

    这是不可能的。一个 MD5 哈希是 128 位;一个 ASCII 字符是 7 位。没有办法将 MD5 哈希存储在任何小于 128÷7 = 18.2(向上取整为 19)的 ASCII 字符中,甚至包括不可打印的控制字符。

    【讨论】:

    • 不,我不是这个意思。我确定您不能将 MD5 转换为 ASCII。我在问是否可以将 MD5 哈希分解为 32 个不同的字符,然后找到 32 个字符中每个字符的 ASCII,也许我可以使用更高的基数重新整合它们。
    • 基本转换在这里没有任何区别。一个 ASCII 字符中只有 7 位信息,不管你如何分割它。 (从某种意义上说,以这种方式使用 ASCII 本质上是将数据转换为 base 128。)
    猜你喜欢
    • 2012-11-05
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多