关于php:将自动递增ID转换为9位随机序列号

Convert auto increment ID to 9 digit random serial number

我有一个带有用户的MySQL数据库。 每行都有一个唯一的自动增量ID(1,2,3 ...)。 现在,我需要将其转换为唯一且随机的序列号,我可以将其转换回ID-全部使用PHP。

用户ID从1到99999999999(INT(11))。

所有序列号均应至少包含9位数字,且切勿以0开头。
用户应该不容易发现如何猜测有效的序列号。

谢谢 :)


您可以进行一些简单的"加密"。以('秘密')素数p 27407base 17为例。计算基本模p-112897的乘法逆inv。 wolframalpha可以为您做到这一点。

ID到序列号

1
2
serial = id^base mod p
serial = 42^17 % 27407 = 24978

序列号到ID

1
2
id = serial^inv mod p
id = 24978^12897 % 27407 = 42

这可以通过平方求幂快速计算。只能使用0到27407之间的ID(如果不够,请使用更大的质数),并且所有ID都具有唯一的可逆序列号。

为了增加模糊度,您可以将结果与某些值进行XOR。

这不是真正的密码学,只是由于晦涩难懂的愚蠢安全性,但是将需要大多数人付出相当大的努力才能破解。


我不建议您做您想做的事情。

您会看到,通常用autoincrement表示来不断避免冗余数据并仍然保持可读性。

因此,改为更新数据库结构以存储哈希值。该结构可能有点像idhashname等。

在哈希中,您可以使用任何逻辑

1
2
$hash = sha1("secretanswer".$userid);
$trimmedhash = substr($has,0,9); //get a 9 digit random hash

哈希是加密的一种方式,也是有原因的。无论如何,为了验证哈希,您可以再次执行相同的算法

1
2
3
4
5
6
7
8
   $userid ="getthissomehow";
   $hash = sha1("secretanswer".$userid);
   $trimmedhash = substr($has,0,9);

   $prevhash ="asfaf23rfs"; //get previously stored hash somehow
   if($trimmedhash == $prevhash) {
     //valid
   }


最简单的答案是使用带有秘密盐作为键的哈希函数

1
2
3
$secretKey = 'oh no nobody will guess this';
$userId = 312;
$serialNumber = hash('sha256', $secretKey . $userId);

哈希当然是单向的,所以您也必须将序列号存储在数据库中,因为您无法根据给定的序列号计算ID。


这是一个非常有趣的问题。希望我了解您在寻找什么。这是我想出的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
$test_limit = 25;
$test_ids = array(1, 99999999999);
for($i=0; $i<($test_limit-2); $i++) $test_ids[] = mt_rand(1, 99999999999);

foreach($test_ids as $tii=>$ti)
{
  $serial = getSerialUsingId($ti);
  $id = getIdUsingSerial($serial);
  if($id!=$ti) echo 'Test '.($tii+1).' (id: '.$ti.') FAILED! (serial: '.$serial.")\
"
;
  else echo 'Test '.($tii+1).' (id: '.$ti.') was a success! (serial: '.$serial.")\
"
;
}


function getMask($index, $places=8)
{
  $masks = array
  (
    0xac976f4,
    0x1c70f81,
    0x441f67f,
    0x5fb0b87,
    0xf1542d2,
    0xfa28851,
    0x91bbd8c,
    0x30a5448,
    0x46a2708,
    0x5856fbf,
    0x65fa462,
    0xf24337b,
    0xea2c390,
    0x8561da4,
    0x9f77b25
  );

  if($places==4) return $masks[$index] & 0x0000FFFF;
  else return $masks[$index];

}// getMask


function getSerialUsingId($id)
{

  $prepend = '';
  $mask_index = mt_rand(0, 14);

  // 8 hex places can only handle up to 4294967295
  // If the number is greater than than that then get the additional bytes and treat separate
  if($id>0xffffffff)
  {
    $packed = pack('d', $id);
    $hex_pack = unpack('H*', $packed);
    $hex_string = substr($hex_pack[1],4);
    $bytes = array_reverse(explode("\
"
, chunk_split($hex_string, 2,"\
"
)));
    foreach($bytes as $bi=>$b) if(!$b) unset($bytes[$bi]);
    $truncated_bytes = array_splice($bytes,0,count($bytes)-4);
    $truncated = implode('', $truncated_bytes);
    $prepend = dechex(hexdec($truncated) ^ getMask($mask_index, 4));
  }

  $serial = dechex($mask_index+1).$prepend.str_pad(dechex($id ^ getMask($mask_index)), 8, '0', STR_PAD_LEFT);

  return $serial;

}// getSerialUsingId


function getIdUsingSerial($serial)
{
  $mask_index = hexdec($serial[0])-1;
  $serial = substr($serial, 1);
  $prepended = false;
  if(strlen($serial)>9)
  {
    $prepended = substr($serial, 0, 4);
    $serial = substr($serial, 4);
  }
  $id = hexdec($serial) ^ getMask($mask_index);
  if($prepended)
  {
    $unmasked_prepended = dechex(hexdec($prepended) ^ getMask($mask_index, 4));
    $bytes = array_reverse(array_merge
    (
      explode("\
"
, chunk_split($unmasked_prepended, 2,"\
"
)),
      explode("\
"
, chunk_split(dechex($id), 2,"\
"
)),
      array('00','00')
    ));
    foreach($bytes as $bi=>$b) if(!$b) unset($bytes[$bi]);
    $packed = pack('H*', implode('', $bytes));
    $unpacked = unpack('d', $packed);
    $id = $unpacked[1];
  }

  return $id;

}// getIdUsingSerial

基本上,序列号以十六进制表示,第一个十六进制数字确定在其余数字上使用哪个位掩码。这使所有序列的长度为9个字符,但id大于4294967295(0xFFFFFF)时除外,在这种情况下,序列将具有4个附加的十六进制数字,这些数字也将根据第一个数字进行屏蔽。合理?希望它可以满足您非常特殊的要求,或者至少可以接受并根据您的需要进行工作。

运行代码的示例输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Test 1 (id: 1) was a success! (serial: 60fa28850)
Test 2 (id: 99999999999) was a success! (serial: 24db649b1e87e)
Test 3 (id: 487808132) was a success! (serial: 31952aafb)
Test 4 (id: 227726272) was a success! (serial: 40869d847)
Test 5 (id: 836896236) was a success! (serial: 53ef7473e)
Test 6 (id: 958345007) was a success! (serial: 93d750827)
Test 7 (id: 164308905) was a success! (serial: 30d8ad1d6)
Test 8 (id: 715018588) was a success! (serial: 1205727a8)
Test 9 (id: 1127737044) was a success! (serial: 8403db29c)
Test 10 (id: 409934489) was a success! (serial: 81b654ed1)
Test 11 (id: 907129123) was a success! (serial: f3fe6ca06)
Test 12 (id: 720453497) was a success! (serial: b2cae9b1b)
Test 13 (id: 500526447) was a success! (serial: 1171c1b9b)
Test 14 (id: 322340582) was a success! (serial: 119fff012)
Test 15 (id: 1176988677) was a success! (serial: b4078c867)
Test 16 (id: 698755861) was a success! (serial: 92dcc0c1d)
Test 17 (id: 555569451) was a success! (serial: 52e0813f9)
Test 18 (id: 227332917) was a success! (serial: a0809bc8a)
Test 19 (id: 819326158) was a success! (serial: 334941ab1)
Test 20 (id: 659803411) was a success! (serial: d29f10e83)
Test 21 (id: 895574245) was a success! (serial: d3bc3a375)
Test 22 (id: 539979792) was a success! (serial: 425d47b97)
Test 23 (id: 933093554) was a success! (serial: 83497b4fa)
Test 24 (id: 959556569) was a success! (serial: 93d5b8cd1)
Test 25 (id: 668064949) was a success! (serial: 22616d334)

首先,因为您希望能够将其从X转换为Y并将其从Y转换为X,所以它不能是随机的。可以按照以下步骤进行数学操作:

1.使用常量数字或ID本身中的数字将ID的长度补全为9。
提醒您:您不能使用随机数字!否则,它不会同时起作用。

2.进行数学运算,例如将每个数字增加1。
请注意,如果此数学操作完全更改一位数字,则几乎
无法取回。
例如,如果您将每个数字加2,然后又有几个数字:8,9
除非您使用"结转"方法,否则您将遇到麻烦。
因此,我建议将每个数字增加X,但如果DIGIT + X> 10,则将其保留为DIGIT。

3.如何转换回去?只需简单地向后执行这些步骤。