【问题标题】:Unique, unpredictable, 12 digit, integer id唯一的、不可预测的、12 位整数 id
【发布时间】:2011-11-03 21:39:48
【问题描述】:

我将如何生成此...

它不能只是自动递增的原因是我不希望序列号很容易被猜到。

它必须是整数,因为我将有需要在电话板上拨打的验证码。

【问题讨论】:

  • 随机数生成器?范围如此之大,独特性真的那么重要吗?
  • @Kerrek:你在说什么?他正在与 DB 打交道。他必须有独特性。
  • @Ajeet:该数字不用于密钥,听起来 OP 出于安全原因只想要唯一性。
  • @Kerrek:或者因为它是一个 ID。不是主键,而是一个 ID。
  • 那么,你想要一个连续的不可预知的数字吗?这几乎是不可能的,因为根据定义,序列是可预测的。

标签: php mysql random


【解决方案1】:

使用一个唯一的递增数字和一个随机生成的数字的串联。

唯一的递增数字确保结果唯一,随机生成的数字使其难以猜测。

这是简单并且保证有没有碰撞 (1)。结果是增量的、部分随机的、不可预测的(前提是随机数部分是用良好的 PRNG 生成的)。

(1):您必须用零填充idrandom,或者用一些非数字字符分隔它们。

使用 MySQL 数据库,这转换为:

CREATE TABLE foo (
    id int not null auto_increment,
    random int not null,
    ...
    primary key (id)
);

【讨论】:

  • 很酷,谢谢。不知道为什么我没想到。使用这种方法,我什至可以“愚蠢地”生成一个完整的 11 位随机数,将其连接起来,并截断任何尾随数字,它仍然是唯一的。
  • 随机生成是不可预测和非连续的。它可能是增量的,但不是顺序的。
  • 嗯...实际上我看到了这个问题。例如,如果 id 是 1 10 或 100,则附加的数字可能会使两个项目相同(尽管不太可能)。例如,如果 id 9 附加了 910000000000,然后 99 附加了 100000000000,则组合键将是相同的。
  • 这种方法保证不会发生冲突,只要 id 位数不改变...所以我将 id 从 100,000 开始,并使用随机生成的尾随 6 位数。
  • 你是对的,你必须用零填充数字,或者用-分隔它们。
【解决方案2】:

到目前为止,所有解决方案都缺少对您的应用程序至关重要的一件事:安全性

您说您将使用这些数字作为(产品)验证码 - 所以您真的非常希望这是不可预测的,否则它被利用。

无论是 MySQL 的内置 RANDOM 函数还是 PHP 今天提供的任何随机函数都不是安全随机函数。它们的行为是伪随机的,好吧,但它们都可预测的!

您唯一的机会是在 *nix 机器上使用 /dev/urandom 或在 Windows 上利用 Crypto API 来创建自己的东西。 OpenSSL 确实提供了基于这些机制的安全随机数 - 您可以在 PHP 的 C 扩展中或通过读取从 PHP 调用的命令行脚本的输出来重用它。另见answer

关于您对数字连续的要求 - 这真的那么重要吗?它确实使事情变得非常复杂。否则,您最好使用使用十六进制编码(产生 12 个字符的字符串)编码为字符串的简单安全 6 字节随机数。尽管我建议将其设置为 10 个字节和 20 个字符以更安全。

但是,如果您想按顺序排列,我将其解释为单调递增(因为简单的 +1 很容易预测),这会使事情变得复杂得多。而且您不会从这种复杂性中获得任何好处,唯一可能发生的事情是您通过发明一些易于利用的晦涩方案来破坏安全性。

我的建议:添加另一列作为普通的旧自动递增 ID,并将代码添加为如上所述构造的随机数作为单独的列。据我所知,没有必要同时要求产品激活码是ID。

【讨论】:

  • 验证号确实是单独一列。我正在考虑将它作为一个随机数和主键的组合,以确保它始终是唯一的(因为如果你一直拉随机数最终会发生冲突)。
  • 两个 12 字节随机数发生冲突的可能性可以忽略不计。不用担心——它最多会在数据库插入时产生错误,如果发生这种情况,您只需重试。但是拜托,我是认真的,如果需要尝试利用您的方案(节省/赚钱等),请不要依赖 MySQL 或 PHP 的内置函数生成的任何随机数 -你会后悔的!
【解决方案3】:

一般来说,我更愿意做一些技术含量更低的事情。我隐藏了 PHP 中的值,并将它们保留为 JS 中的自动递增。

$seeds = array( /*series 100 of very large >= 10-digit numbers*/ );
$seedID = rand( count( $seeds ) ); // randomly choose one of those.
// a string combination which represents the ID + some hash.
$id = bcadd( $seeds[ $seedID ], /* id retrieved from database */ );
// make sure we've not accidentally passed the 10^12 point
$id = bcmod( $id, 1000000000000 );
// make sure to pad
$id = str_pad('' .  $id, 3, "0", STR_PAD_LEFT);
$outID = substr( $id, 0, 5 ) . $seedID . substr( $id, 6 );

那么,当接收到用户的ID时:

$seedID = substr( $outID, 6, 2 );
$tmpID = substr( $outID, 0, 5 ) . substr( $outID, 8 );
$id = bcsub( $tmpID, $seeds[ $seedID ] );
// we passed the modulus se we need to add this back in.
if( $id < 0 ) $id = bcmod( bcadd( $id, 1000000000000 ), 1000000000000 );

这基本上意味着您只是在模糊您想要的任何数字——您可以使用 auto_increment 而不受惩罚!

【讨论】:

    【解决方案4】:

    也许你可以使用UUID_SHORT()。不是 12 位数长,但仍然可能是一个可行的选择:

    mysql> select uuid_short();
    +-------------------+
    | uuid_short()      |
    +-------------------+
    | 22048742962102272 |
    +-------------------+
    

    所以:

    INSERT INTO `table` (`id`, `text`) VALUES (UUID_SHORT(), 'hello world!');
    

    注意:如果你真的想要精确到 12 位,那么甚至不要尝试对结果进行子串化,否则无法确保标识符的唯一性,并且可能会导致冲突。

    【讨论】:

    • 这不过是全局递增的数字。这是高度可预测的,看看它是如何生成的:(server_id &amp; 255) &lt;&lt; 56 + (server_startup_time_in_seconds &lt;&lt; 24) + incremented_variable++;
    • 是的,我知道。还是比1,2,3,4好。此外,根据定义,顺序可预测的。
    • 你们有没有读过这个问题?你错过了顺序的哪一部分?随机不能是连续的。
    • @netcoder OP 要求一个不可预测的数字;)我的回答给出的数字既是连续的又是不可预测的。
    • @arnaud576875:很有趣,因为我在那个问题的任何地方都没有看到“随机”这个词。你不可能有连续的随机数,它们实际上是相反的。
    【解决方案5】:

    你想要两件事

    1. 独特性
    2. 增量

    如果你想要两个相同序列的东西,你会运气不好(字面意思) 唯一性是通过拥有大样本空间 + 随机 + 检查唯一性来保证的。这意味着,实际数字可能在样本空间之间的任何位置。

    但是,如果您想要唯一 + 增量属性,则将样本空间除以 2。在 64 次尝试中,您会将 64 位 int 样本空间减少到 1 位样本空间。

    祝你好运!

    【讨论】:

    • 他说-sequential。而且我不明白样本空间在哪里除以 2。
    • @Tomalak Geret'kal:他实际上说的是sequential
    【解决方案6】:

    一种方法是获取您的主键值,用其他一些随机数据(用户名、当前时间、进程 ID、固定字符串等)对其进行加盐,然后使用 md5 或 sha1 对其进行哈希处理.然后,您获取哈希字符串并通过基本字符串操作将其转换为数字。这会给你一个相对独特的数字代码。

    当然,只有 12 位数字,与使用原始字符串散列相比,您更有可能最终发生冲突 - 但由于您需要在键盘上拨号,因此这是一个可以接受的折衷方案。

    如果引脚在使用后失效/删除,那么碰撞机会将大大减少。

    【讨论】:

      【解决方案7】:
      <?php 
      $allowed_characters = array(1,2,3,4,5,6,7,8,9,0);
      for($i = 1;$i <= 12; $i++){
          $pass .= $allowed_characters[rand(0, count($allowed_characters) - 1)];
      }
      echo $pass;
      ?>
      

      演示:http://sandbox.phpcode.eu/g/c0190/4

      【讨论】:

        猜你喜欢
        • 2010-10-15
        • 2015-02-28
        • 1970-01-01
        • 1970-01-01
        • 2011-07-30
        • 2011-01-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多