PHP:如何生成一个随机的,唯一的字母数字字符串?


380

如何使用数字和字母生成一个随机的,唯一的字符串以用于验证链接?就像您在网站上创建帐户一样,该帐户会向您发送包含链接的电子邮件,您必须单击该链接才能验证您的帐户...是的...其中之一。

如何使用PHP生成其中之一?

更新:刚刚记得uniqid()。这是一个PHP函数,可根据当前时间(以微秒为单位)生成唯一标识符。我想我会用的。


您只需要字符串和均匀分布的随机数即可。
Artelius

10
嗨,安德鲁,您应该选择Scott作为正确答案。Scott正在使用OpenSSL的加密安全伪随机数生成器(CSPRNG),它将根据您的平台选择最安全的熵源。
菜鸟

3
任何在2015年之后阅读
此书的人

OpenSSL uniqid并不安全。使用类似Random::alphanumericString($length)或的东西Random::alphanumericHumanString($length)
Caw

Answers:


305

安全声明:如果您的随机性会影响应用程序的安全性,则不应使用此解决方案。特别地,rand()并且uniqid()不是加密安全的随机数生成器。有关安全替代方案,请参见Scott的答案

如果您不需要它随着时间的流逝而变得绝对独特,请执行以下操作:

md5(uniqid(rand(), true))

否则(假设您已经为用户确定了唯一的登录名):

md5(uniqid($your_user_login, true))

90
两种方法都不保证唯一性-md5函数的输入长度大于其输出的长度,并且根据en.wikipedia.org/wiki/Pigeonhole_principle,可以确保冲突。另一方面,依靠散列实现“唯一”标识的人群越多,发生至少一次碰撞的可能性就越大(请参阅en.wikipedia.org/wiki/Birthday_problem)。对于大多数解决方案而言,这种可能性可能很小,但仍然存在。
Dariusz Walczak

12
这不是生成随机值的安全方法。请参阅Scott的答案。
菜鸟

4
对于相对较快到期的电子邮件确认,我认为一个人一天有可能确认其他人的电子邮件的可能性很小,风险很小。但是创建令牌后,您始终可以检查令牌是否已存在于数据库中,如果是这种情况,只需选择一个新令牌即可。但是,您花费在编写该代码段上的时间可能很浪费,因为它很可能永远不会运行。
2014年

请注意,md5返回十六进制值,这意味着字符集限制为[0-9]和[af]。
Thijs Riezebeek

1
md5可能会发生冲突,您刚刚毁了
唯一

507

我只是在研究如何解决相同的问题,但是我也希望我的函数创建一个令牌,该令牌也可以用于密码检索。这意味着我需要限制令牌被猜测的能力。因为uniqid基于时间,并且根据php.net“返回值与microtime()几乎没有区别”,uniqid所以不符合标准。PHP建议openssl_random_pseudo_bytes()改为使用来生成加密安全令牌。

一个快速,简短而切题的答案是:

bin2hex(openssl_random_pseudo_bytes($bytes))

它将生成一个随机字符串,由长度为$ bytes * 2的字母数字字符组成。不幸的是,它只有一个字母[a-f][0-9],但是可以使用。


以下是我可以满足标准的最强大功能(这是Erik的答案的实现版本)。

function crypto_rand_secure($min, $max)
{
    $range = $max - $min;
    if ($range < 1) return $min; // not so random...
    $log = ceil(log($range, 2));
    $bytes = (int) ($log / 8) + 1; // length in bytes
    $bits = (int) $log + 1; // length in bits
    $filter = (int) (1 << $bits) - 1; // set all lower bits to 1
    do {
        $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
        $rnd = $rnd & $filter; // discard irrelevant bits
    } while ($rnd > $range);
    return $min + $rnd;
}

function getToken($length)
{
    $token = "";
    $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
    $codeAlphabet.= "0123456789";
    $max = strlen($codeAlphabet); // edited

    for ($i=0; $i < $length; $i++) {
        $token .= $codeAlphabet[crypto_rand_secure(0, $max-1)];
    }

    return $token;
}

crypto_rand_secure($min, $max)用作rand()或的替代品mt_rand。它使用openssl_random_pseudo_bytes帮助创建介于$ min和$ max之间的随机数。

getToken($length)创建在令牌中使用的字母,然后创建一个长度的字符串$length

编辑:我忽略了引用源-http: //us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322

编辑(PHP7):随着PHP7的发布,标准库现在具有两个新功能,可以替换/改进/简化上面的crypto_rand_secure函数。random_bytes($length)random_int($min, $max)

http://php.net/manual/zh/function.random-bytes.php

http://php.net/manual/zh/function.random-int.php

例:

function getToken($length){
    $token = "";
    $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
    $codeAlphabet.= "0123456789";
    $max = strlen($codeAlphabet);

    for ($i=0; $i < $length; $i++) {
        $token .= $codeAlphabet[random_int(0, $max-1)];
    }

    return $token;
}

2
同意 纯文本密码存储非常糟糕!(我使用河豚鱼。)但是在生成令牌后,它允许令牌的持有者重设密码,因此,令牌在有效时与密码等效(并且被视为令牌)。
斯科特,

23
嘿,我将您的出色代码与便捷的Kohana Text :: random()方法功能结合在一起,并创建了以下要点:gist.github.com/raveren/5555297
raveren

9
完善!我测试了10个字符作为长度。100000个令牌,仍然没有重复!
2014年

5
我使用了此过程,发现它非常慢。由于长度为36,因此在开发服务器上超时。长度为24时,仍需要30秒左右。不知道我做错了什么,但是对我来说太慢了,我选择了另一个解决方案。
Shane 2014年

5
您也可以使用base64_encode代替bin2hex以获得带有较大字母的较短字符串。
erjiang

91

最具争议的解决方案的面向对象版本

我已经基于Scott的答案创建了一个面向对象的解决方案:

<?php

namespace Utils;

/**
 * Class RandomStringGenerator
 * @package Utils
 *
 * Solution taken from here:
 * http://stackoverflow.com/a/13733588/1056679
 */
class RandomStringGenerator
{
    /** @var string */
    protected $alphabet;

    /** @var int */
    protected $alphabetLength;


    /**
     * @param string $alphabet
     */
    public function __construct($alphabet = '')
    {
        if ('' !== $alphabet) {
            $this->setAlphabet($alphabet);
        } else {
            $this->setAlphabet(
                  implode(range('a', 'z'))
                . implode(range('A', 'Z'))
                . implode(range(0, 9))
            );
        }
    }

    /**
     * @param string $alphabet
     */
    public function setAlphabet($alphabet)
    {
        $this->alphabet = $alphabet;
        $this->alphabetLength = strlen($alphabet);
    }

    /**
     * @param int $length
     * @return string
     */
    public function generate($length)
    {
        $token = '';

        for ($i = 0; $i < $length; $i++) {
            $randomKey = $this->getRandomInteger(0, $this->alphabetLength);
            $token .= $this->alphabet[$randomKey];
        }

        return $token;
    }

    /**
     * @param int $min
     * @param int $max
     * @return int
     */
    protected function getRandomInteger($min, $max)
    {
        $range = ($max - $min);

        if ($range < 0) {
            // Not so random...
            return $min;
        }

        $log = log($range, 2);

        // Length in bytes.
        $bytes = (int) ($log / 8) + 1;

        // Length in bits.
        $bits = (int) $log + 1;

        // Set all lower bits to 1.
        $filter = (int) (1 << $bits) - 1;

        do {
            $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));

            // Discard irrelevant bits.
            $rnd = $rnd & $filter;

        } while ($rnd >= $range);

        return ($min + $rnd);
    }
}

用法

<?php

use Utils\RandomStringGenerator;

// Create new instance of generator class.
$generator = new RandomStringGenerator;

// Set token length.
$tokenLength = 32;

// Call method to generate random string.
$token = $generator->generate($tokenLength);

自定义字母

您可以根据需要使用自定义字母。只需将带有支持字符的字符串传递给构造函数或setter:

<?php

$customAlphabet = '0123456789ABCDEF';

// Set initial alphabet.
$generator = new RandomStringGenerator($customAlphabet);

// Change alphabet whenever needed.
$generator->setAlphabet($customAlphabet);

这是输出样本

SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
duoRF6gAawNOEQRICnOUNYmStWmOpEgS
sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
eVywMdYXczuZmHaJ50nIVQjOidEVkVna
baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa

希望对您有所帮助。干杯!


对不起,我该怎么办?我做到了,$obj = new RandomStringGenerator;但是我应该调用哪种方法?谢谢。
sg552 2014年

@ sg552,您应该generate像上面的示例中那样调用方法。只需向其传递一个整数即可指定结果字符串的长度。
Slava Fomin II

谢谢它对我有用,但Custom Alphabet没有。回显时输出为空。无论如何,我什至不知道第二个示例的用途是什么,Custom Alphabet所以我认为我只保留第一个示例。谢谢。
sg552 2014年

1
不错,但是在大多数情况下都

1
是不错,但矫枉过正,特别是考虑到现在是非常过时的PHP 5.7
安德鲁

39

我来晚了,但是我在这里有一些很好的研究数据,这些数据基于Scott的答案提供的功能。因此,我为此为期5天的自动测试设置了一个Digital Ocean小滴,并将生成的唯一字符串存储在MySQL数据库中。

在此测试期间,我使用了5种不同的长度(5、10、15、20、50),并且为每种长度插入了+/- 0.5百万条记录。在我的测试过程中,只有5个长度生成了50万个+/- 3K重复,其余长度没有生成任何重复。因此,可以说,如果我们将长度等于或大于15的Scott函数使用,则可以生成高度可靠的唯一字符串。这是显示我的研究数据的表:

在此处输入图片说明

更新:我使用这些函数创建了一个简单的Heroku应用,该应用将令牌作为JSON响应返回。可以通过https://uniquestrings.herokuapp.com/api/token?length=15访问该应用程序

我希望这有帮助。


33

您可以使用UUID(通用唯一标识符),它可以用于任何目的,从用户身份验证字符串到付款交易ID。

UUID是一个16字节(128位)的数字。UUID以其规范形式由32个十六进制数字表示,以连字符分隔的5个组显示,形式为8-4-4-4-12,共36个字符(32个字母数字字符和4个连字符)。

function generate_uuid() {
    return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
        mt_rand( 0, 0xffff ),
        mt_rand( 0, 0x0C2f ) | 0x4000,
        mt_rand( 0, 0x3fff ) | 0x8000,
        mt_rand( 0, 0x2Aff ), mt_rand( 0, 0xffD3 ), mt_rand( 0, 0xff4B )
    );

}

//调用功能

$transationID = generate_uuid();

一些示例输出如下所示:

E302D66D-87E3-4450-8CB6-17531895BF14
22D288BC-7289-442B-BEEA-286777D559F2
51B4DE29-3B71-4FD2-9E6C-071703E1FF31
3777C8C6-9FF5-4C78-AAA2-08A47F555E81
54B91C72-2CF4-4501-A6E9-02A60DCBAE4C
60F75C7C-1AE3-417B-82C8-14D456542CD7
8DE0168D-01D3-4502-9E59-10D665CEBCB2

希望以后能对某人有所帮助:)


看起来很棒,已添加到我的列表中!
穆斯塔法·埃尔多安

32

此函数将使用数字和字母生成随机密钥:

function random_string($length) {
    $key = '';
    $keys = array_merge(range(0, 9), range('a', 'z'));

    for ($i = 0; $i < $length; $i++) {
        $key .= $keys[array_rand($keys)];
    }

    return $key;
}

echo random_string(50);

输出示例:

zsd16xzv3jsytnp87tk7ygv73k8zmr0ekh6ly7mxaeyeh46oe8

6
一段时间后,您得到的数字是相同的
Sharpless512 2014年

1
这不是完全随机的,因为它永远不会重复拥有相同的角色。
EricP 2014年

16

我使用这种单线:

base64_encode(openssl_random_pseudo_bytes(3 * ($length >> 2)));

其中length是所需字符串的长度(可被4整除,否则将向下舍入为最接近的可被4整除的数字)


6
它并不总是仅返回字母数字字符。它可以包含类似字符==
user3459110 2014年

1
如果在URL中使用,base64url_encode则可以代替使用,请参见此处:php.net/manual/en/function.base64-encode.php
Paul Paul

9

使用下面的代码生成11个字符的随机数或根据您的要求更改数字。

$randomNum=substr(str_shuffle("0123456789abcdefghijklmnopqrstvwxyz"), 0, 11);

或者我们可以使用自定义函数生成随机数

 function randomNumber($length){
     $numbers = range(0,9);
     shuffle($numbers);
     for($i = 0;$i < $length;$i++)
        $digits .= $numbers[$i];
     return $digits;
 }

 //generate random number
 $randomNum=randomNumber(11);

3
这永远不会生成字符串“ aaaaaaaaaaa”或“ aaaaabbbbbb”。它只是选择字母随机排列的子字符串。长11个字符的字符串仅具有V(11,35)= 288个可能的值。如果您期望字符串是唯一的,请不要使用它!
kdojeteri

@Peping然后去做substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstvwxyz', 36)), 0, 11);
Tim Hallman

7
  1. 使用您喜欢的随机数生成器生成随机数
  2. 将其相乘并除以得到与代码字母中的字符数匹配的数字
  3. 在您的代码字母中的索引处获取项目。
  4. 从1)开始重复,直到达到所需的长度

例如(用伪代码)

int myInt = random(0, numcharacters)
char[] codealphabet = 'ABCDEF12345'
char random = codealphabet[i]
repeat until long enough

从理论上讲,这难道不会多次产生相同的字符串吗?
frezq

4

这是您的终极独特ID生成器。我做的。

<?php
$d=date ("d");
$m=date ("m");
$y=date ("Y");
$t=time();
$dmt=$d+$m+$y+$t;    
$ran= rand(0,10000000);
$dmtran= $dmt+$ran;
$un=  uniqid();
$dmtun = $dmt.$un;
$mdun = md5($dmtran.$un);
$sort=substr($mdun, 16); // if you want sort length code.

echo $mdun;
?>

您可以根据需要在ID中回显任何“ var”。但是$ mdun更好,您可以将md5替换为sha1以获得更好的代码,但这可能会很长,您可能不需要。

谢谢。


7
在这种情况下,可以通过随机编写的代码来保证随机性,对吗?
Daniel Kucal

3
是的,这是一种“随机”,因为它是非标准的,晦涩的代码。这使得难以预测...但实际上并非使用不安全的部分来生成随机数。因此,从技术上讲,结果不是随机的或安全的。
菲利普

4

对于真正随机的字符串,您可以使用

<?php

echo md5(microtime(true).mt_Rand());

输出:

40a29479ec808ad4bcff288a48a25d5c

因此,即使您尝试在同一时间多次生成字符串,您也会获得不同的输出。


4

尝试生成随机密码时,您尝试:

  • 首先生成一个加密安全的随机字节集

  • 其次是将这些随机字节转换为可打印的字符串

现在,有多种方法可以在php中生成随机字节,例如:

$length = 32;

//PHP 7+
$bytes= random_bytes($length);

//PHP < 7
$bytes= openssl_random_pseudo_bytes($length);

然后,您要将这些随机字节转换为可打印的字符串:

您可以使用bin2hex

$string = bin2hex($bytes);

base64_encode

$string = base64_encode($bytes);

但是,请注意,如果使用base64,则不能控制字符串的长度。您可以使用bin2hex来做到这一点,使用32个字节将变成一个64个字符的字符串。但是,即使EVEN字符串中也只能这样工作。

所以基本上你可以做

$length = 32;

if(PHP_VERSION>=7){
    $bytes= random_bytes($length);
}else{
    $bytes= openssl_random_pseudo_bytes($length);
} 

$string = bin2hex($bytes);


2
function random_string($length = 8) {
    $alphabets = range('A','Z');
    $numbers = range('0','9');
    $additional_characters = array('_','=');
    $final_array = array_merge($alphabets,$numbers,$additional_characters);
       while($length--) {
      $key = array_rand($final_array);

      $password .= $final_array[$key];
                        }
  if (preg_match('/[A-Za-z0-9]/', $password))
    {
     return $password;
    }else{
    return  random_string();
    }

 }

2

这是一个简单的函数,可让您生成包含字母和数字(字母数字)的随机字符串。您还可以限制字符串长度。这些随机字符串可用于各种目的,包括:推荐代码,促销代码,优惠券代码。函数依赖于以下PHP函数: base_convert,sha1,uniqid,mt_rand

function random_code($length)
{
  return substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
}

echo random_code(6);

/*sample output
* a7d9e8
* 3klo93
*/

1

阅读之前的示例后,我想到了:

protected static $nonce_length = 32;

public static function getNonce()
{
    $chars = array();
    for ($i = 0; $i < 10; $i++)
        $chars = array_merge($chars, range(0, 9), range('A', 'Z'));
    shuffle($chars);
    $start = mt_rand(0, count($chars) - self::$nonce_length);
    return substr(join('', $chars), $start, self::$nonce_length);
}

我将array [0-9,AZ]复制了10倍,并随机排列了元素,在我得到substr()的随机起点以使其更具“创造力”之后:)您可以向数组中添加[az]和其他元素,重复或多或少,比我更有创造力


1

我喜欢在处理验证链接时使用哈希键。我建议使用microtime并使用MD5进行哈希处理,因为没有理由为什么密钥应该相同,因为它基于microtime进行哈希处理。

  1. $key = md5(rand());
  2. $key = md5(microtime());

1
<?php
function generateRandomString($length = 11) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;

}

?>

上面的函数将为您生成一个长度为11个字符的随机字符串。


1

如果要在PHP中生成唯一字符串,请尝试以下操作。

md5(uniqid().mt_rand());

在这个

uniqid()-它将生成唯一的字符串。此函数将基于时间戳的唯一标识符作为字符串返回。

mt_rand() -生成随机数。

md5() -它将生成哈希字符串。


0

斯科特,是的,您非常有才华,而且是很好的解决方案!谢谢。

我还需要为每个用户生成唯一的API令牌。以下是我的方法,我使用了用户信息(Userid和Username):

public function generateUniqueToken($userid, $username){

        $rand = mt_rand(100,999);
    $md5 = md5($userid.'!(&^ 532567_465 ///'.$username);

    $md53 = substr($md5,0,3);
    $md5_remaining = substr($md5,3);

    $md5 = $md53. $rand. $userid. $md5_remaining;

    return $md5;
}

请看一下,让我知道我是否可以做任何改善。谢谢


0

这是我在一个项目中使用的东西,它工作得很好,并且会生成一个UNIQUE RANDOM TOKEN

$timestampz=time();

function generateRandomString($length = 60) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}


$tokenparta = generateRandomString();


$token = $timestampz*3 . $tokenparta;

echo $token;

请注意,我将时间戳记乘以三来使任何想知道此令牌是如何生成的用户感到困惑;)

希望对您有所帮助:)


1
“请注意,我将时间戳乘以3来为任何可能想知道如何生成此令牌的用户带来困惑”。听起来像是通过混淆产生的安全性stackoverflow.com/questions/533965/…–
哈里

这不是“通过混淆获得的安全性”,因为时间戳会被添加到生成的随机字符串中,而这是我们返回的实际最终字符串;)
Kaszoni Ferencz 18/10/23



0

我总是使用此函数来生成自定义的随机字母数字字符串...希望获得帮助。

<?php
  function random_alphanumeric($length) {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345689';
    $my_string = '';
    for ($i = 0; $i < $length; $i++) {
      $pos = mt_rand(0, strlen($chars) -1);
      $my_string .= substr($chars, $pos, 1);
    }
    return $my_string;
  }
  echo random_alphanumeric(50); // 50 characters
?>

例如,它生成:Y1FypdjVbFCFK6Gh9FDJpe6dciwJEfV6MQGpJqAfuijaYSZ86g

如果要与另一个字符串进行比较以确保这是一个唯一序列,则可以使用此技巧...

$string_1 = random_alphanumeric(50);
$string_2 = random_alphanumeric(50);
while ($string_1 == $string_2) {
  $string_1 = random_alphanumeric(50);
  $string_2 = random_alphanumeric(50);
  if ($string_1 != $string_2) {
     break;
  }
}
echo $string_1;
echo "<br>\n";
echo $string_2;

它生成两个唯一的字符串:

qsBDs4JOoVRfFxyLAOGECYIsWvpcpMzAO9pypwxsqPKeAmYLOi

Ti3kE1WfGgTNxQVXtbNNbhhvvapnaUfGMVJecHkUjHbuCb85pF

希望这就是您想要的...


0

一个简单的解决方案是通过丢弃非字母数字字符将基数64转换为字母数字。

此代码使用random_bytes()获得加密安全的结果。

function random_alphanumeric(int $length): string
{
    $result='';
    do
    {
        //Base 64 produces 4 characters for each 3 bytes, so most times this will give enough bytes in a single pass
        $bytes=random_bytes(($length+3-strlen($result))*2);
        //Discard non-alhpanumeric characters
        $result.=str_replace(['/','+','='],['','',''],base64_encode($bytes));
        //Keep adding characters until the string is long enough
        //Add a few extra because the last 2 or 3 characters of a base 64 string tend to be less diverse
    }while(strlen($result)<$length+3);
    return substr($result,0,$length);
}


-1

我相信,所有的现有观念的问题是,他们可能唯一的,但不是绝对的独特(如达留沃尔查克的答复中指出,以loletech)。我有一个实际上是独一无二的解决方案。它要求您的脚本具有某种内存。对我来说,这是一个SQL数据库。您也可以只写一个文件到某个地方。有两种实现:

第一种方法:提供两个字段,而不是提供唯一性的1个字段。第一个字段是一个ID号码,该号码不是随机的而是唯一的(第一个ID为1,第二个为2 ...)。如果使用的是SQL,则只需使用AUTO_INCREMENT属性定义ID字段。第二个字段不是唯一的,而是随机的。这可以通过人们已经提到的任何其他技术来生成。Scott的想法很好,但是md5很方便,对于大多数用途来说可能足够好:

$random_token = md5($_SERVER['HTTP_USER_AGENT'] . time());

第二种方法:基本上是相同的想法,但是最初选择了将要生成的最大字符串数。这可能是一个非常大的数字,例如一万亿。然后执行相同的操作,生成一个ID,但是将其填充零以使所有ID都是相同的位数。然后只需将ID与随机字符串连接即可。对于大多数用途来说,它是足够随机的,但是ID部分将确保它也是唯一的。


-1

您可以使用此代码,希望对您有所帮助。

function rand_code($len)
{
 $min_lenght= 0;
 $max_lenght = 100;
 $bigL = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 $smallL = "abcdefghijklmnopqrstuvwxyz";
 $number = "0123456789";
 $bigB = str_shuffle($bigL);
 $smallS = str_shuffle($smallL);
 $numberS = str_shuffle($number);
 $subA = substr($bigB,0,5);
 $subB = substr($bigB,6,5);
 $subC = substr($bigB,10,5);
 $subD = substr($smallS,0,5);
 $subE = substr($smallS,6,5);
 $subF = substr($smallS,10,5);
 $subG = substr($numberS,0,5);
 $subH = substr($numberS,6,5);
 $subI = substr($numberS,10,5);
 $RandCode1 = str_shuffle($subA.$subD.$subB.$subF.$subC.$subE);
 $RandCode2 = str_shuffle($RandCode1);
 $RandCode = $RandCode1.$RandCode2;
 if ($len>$min_lenght && $len<$max_lenght)
 {
 $CodeEX = substr($RandCode,0,$len);
 }
 else
 {
 $CodeEX = $RandCode;
 }
 return $CodeEX;
}

有关PHP中随机代码生成器的详细信息


-2

通过消除不必要的循环来简化上面的Scotts代码,这会严重减慢速度,并且与仅一次调用openssl_random_pseudo_bytes相比并没有使其更加安全

function crypto_rand_secure($min, $max)
{
 $range = $max - $min;
 if ($range < 1) return $min; // not so random...
 $log = ceil(log($range, 2));
 $bytes = (int) ($log / 8) + 1; // length in bytes
 $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
 return $min + $rnd%$range;
}

function getToken($length)
{
 return bin2hex(openssl_random_pseudo_bytes($length)
}

不幸的是,我不同意这是crypto_rand_secure函数的适当简化。循环是必要的,这样可以避免在取随机生成的数字的结果,然后将其修改为确实会划分范围的数字时发生模偏差。en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Modulo_bias。通过使用循环抛出范围以外的任何数字并选择一个新数字,可以避免这种偏差。stackoverflow.com/questions/10984974/…–
斯科特(Scott)

@Scott,那个帖子是关于rand()的。但是,在这种情况下,openssl_random_pseudo_bytes已经对其进行了处理,因此不需要循环。
mabel

1
gist.github.com/anonymous/87e3ae6fd3320447f46b973e75c2ea85-请运行此脚本。您将看到结果的分布不是随机的。0-55之间的数字比56-100之间的数字更高。openssl不会导致这种情况,这是因为return语句中的模偏差。100(范围)不能平均分割255(1字节)并导致这些结果。我的最大问题是您声明该函数与使用循环一样安全,这是错误的。您对crypto_rand_secure的实现不是加密安全的,并且具有误导性。
斯科特(Scott)

@Scott,我们在这里讲题,它与最后一个return语句无关,它也可以从openssl_random_pseudo_bytes的返回值逐步计算而无需使用模数。我要说的是,一旦openssl_random_pseudo_bytes返回随机字节,则不需要循环。通过制作循环,您并没有使其变得更安全,而只是使其变得缓慢。
mabel

2
此答案传播错误的信息。您的openssl回答是正确的,即循环不会使安全性提高,但更重要的是,循环并不能使其安全性降低。上面的代码采用了一个非常好的随机数,并通过将其偏向较小的数字来对其进行处理。如果您在回答中放一个免责声明,说明它更快(通过删除循环),但它不是CSPRNG,则可以解决此问题。请热爱加密,让用户知道此函数的输出可能会有偏差,而不是等效的解决方案。循环不只是烦人。
斯科特(Scott)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.