减少 MD5 - 使用不同的基础
Reduce MD5 - Using a different base
我的客户生成的促销优惠券代码不过是 32 个字符的 MD5 哈希值。
我的工作是将 MD5 字符串从 32 个字符减少到少于 10 个字符,这样可以从减少的字符串中重新创建散列。
减少很重要,因为用户可以更轻松地重现减少的哈希值。
例如:719bedacf2e560b27f39d80accc67ffd => ZjKa1Gh
(数学上不正确)
我遇到了这个:How to reduce hash value's length?
它建议:使用不同的基数
我不知道如何在 PHP 中执行此操作,我们可以将字符串解码为其 ASCII 并重新编码吗?
PHP 中是否有我可以在这种情况下使用的内置函数?
Update using 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 个字符的方法。
Update: Using first half of 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。如果它 returns 连续我可以继续促销 activity。
密码哈希实际上是一个位序列,但它可以解释为一个数字。因此,理论上您可以使用旧的 base_convert() 来表示基数很大。不幸的是,此函数仅适用于 36 进制,并且仅限于实际数字(即适合 PHP_INT_MAX
的短整数)——否则会丢失数据。
这就是第三方库可以提供帮助的地方。唯一的问题是它们往往很难找到,因为它们通常处理非常具体的用例(比特币、ID 混淆等)。
我发现例如aza/math,这可能有点矫枉过正,但应该可以完成工作。我还没有机会测试它,但它应该是这样的:
$original = '719bedacf2e560b27f39d80accc67ffd';
$short = NumeralSystem::convert($original, 16, 62);
$recovered = NumeralSystem::convert($short, 62, 16);
使用不同碱基的方法可以如下进行。请注意,下面的代码只是为了说明该方法,为了有效地实现它,需要直接使用二进制表示。
这个想法是将输入字符串解释为 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;
?>
如评论中所述,这基本上是背后的想法:
<?php
$s = "719bedacf2e560b27f39d80accc67ffd";
echo base64_encode(hex2bin($s));
//cZvtrPLlYLJ/OdgKzMZ//Q==
echo bin2hex(base64_decode("cZvtrPLlYLJ/OdgKzMZ//Q=="));
//719bedacf2e560b27f39d80accc67ffd
?>
My job is to reduce the MD5 string from 32 chars to less than 10 chars in a way that the hash can be recreated from the reduced string.
那是不可能的。 MD5 散列为 128 位;一个 ASCII 字符是 7 位。无法将 MD5 哈希值存储在少于 128÷7 = 18.2(四舍五入到 19)个 ASCII 字符中,甚至包括不可打印的控制字符。
我的客户生成的促销优惠券代码不过是 32 个字符的 MD5 哈希值。
我的工作是将 MD5 字符串从 32 个字符减少到少于 10 个字符,这样可以从减少的字符串中重新创建散列。
减少很重要,因为用户可以更轻松地重现减少的哈希值。
例如:719bedacf2e560b27f39d80accc67ffd => ZjKa1Gh
(数学上不正确)
我遇到了这个:How to reduce hash value's length?
它建议:使用不同的基数
我不知道如何在 PHP 中执行此操作,我们可以将字符串解码为其 ASCII 并重新编码吗?
PHP 中是否有我可以在这种情况下使用的内置函数?
Update using 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 个字符的方法。
Update: Using first half of 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。如果它 returns 连续我可以继续促销 activity。
密码哈希实际上是一个位序列,但它可以解释为一个数字。因此,理论上您可以使用旧的 base_convert() 来表示基数很大。不幸的是,此函数仅适用于 36 进制,并且仅限于实际数字(即适合 PHP_INT_MAX
的短整数)——否则会丢失数据。
这就是第三方库可以提供帮助的地方。唯一的问题是它们往往很难找到,因为它们通常处理非常具体的用例(比特币、ID 混淆等)。
我发现例如aza/math,这可能有点矫枉过正,但应该可以完成工作。我还没有机会测试它,但它应该是这样的:
$original = '719bedacf2e560b27f39d80accc67ffd';
$short = NumeralSystem::convert($original, 16, 62);
$recovered = NumeralSystem::convert($short, 62, 16);
使用不同碱基的方法可以如下进行。请注意,下面的代码只是为了说明该方法,为了有效地实现它,需要直接使用二进制表示。
这个想法是将输入字符串解释为 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;
?>
如评论中所述,这基本上是背后的想法:
<?php
$s = "719bedacf2e560b27f39d80accc67ffd";
echo base64_encode(hex2bin($s));
//cZvtrPLlYLJ/OdgKzMZ//Q==
echo bin2hex(base64_decode("cZvtrPLlYLJ/OdgKzMZ//Q=="));
//719bedacf2e560b27f39d80accc67ffd
?>
My job is to reduce the MD5 string from 32 chars to less than 10 chars in a way that the hash can be recreated from the reduced string.
那是不可能的。 MD5 散列为 128 位;一个 ASCII 字符是 7 位。无法将 MD5 哈希值存储在少于 128÷7 = 18.2(四舍五入到 19)个 ASCII 字符中,甚至包括不可打印的控制字符。