PHP: How to generate a random, unique, alphanumeric string for use in a secret link?

后端 未结 28 2103
隐瞒了意图╮
隐瞒了意图╮ 2020-11-21 22:20

How would it be possible to generate a random, unique string using numbers and letters for use in a verify link? Like when you create an account on a website, and it sends y

相关标签:
28条回答
  • 2020-11-21 23:06
    1. Generate a random number using your favourite random-number generator
    2. Multiply and divide it to get a number matching the number of characters in your code alphabet
    3. Get the item at that index in your code alphabet.
    4. Repeat from 1) until you have the length you want

    e.g (in pseudo code)

    int myInt = random(0, numcharacters)
    char[] codealphabet = 'ABCDEF12345'
    char random = codealphabet[i]
    repeat until long enough
    
    0 讨论(0)
  • 2020-11-21 23:06

    I think this is the best method to use.

    str_shuffle(md5(rand(0,100000)))
    
    0 讨论(0)
  • 2020-11-21 23:07
    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();
        }
    
     }
    
    0 讨论(0)
  • 2020-11-21 23:10

    I believe the problem with all the existing ideas is that they are probably unique, but not definitely unique (as pointed out in Dariusz Walczak's reply to loletech). I have a solution that actually is unique. It requires that your script have some sort of memory. For me this is a SQL database. You could also simply write to a file somewhere. There are two implementations:

    First method: have TWO fields rather than 1 that provide uniqueness. The first field is an ID number that is not random but is unique (The first ID is 1, the second 2...). If you are using SQL, just define the ID field with the AUTO_INCREMENT property. The second field is not unique but is random. This can be generated with any of the other techniques people have already mentioned. Scott's idea was good, but md5 is convenient and probably good enough for most purposes:

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

    Second method: Basically the same idea, but initially pick a maximum number of strings that will ever be generated. This could just be a really big number like a trillion. Then do the same thing, generate an ID, but zero pad it so that all IDs are the same number of digits. Then just concatenate the ID with the random string. It will be random enough for most purposes, but the ID section will ensure that it is also unique.

    0 讨论(0)
  • 2020-11-21 23:11

    Object-oriented version of the most up-voted solution

    I've created an object-oriented solution based on Scott's answer:

    <?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);
        }
    }
    

    Usage

    <?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);
    

    Custom alphabet

    You can use custom alphabet if required. Just pass a string with supported chars to the constructor or setter:

    <?php
    
    $customAlphabet = '0123456789ABCDEF';
    
    // Set initial alphabet.
    $generator = new RandomStringGenerator($customAlphabet);
    
    // Change alphabet whenever needed.
    $generator->setAlphabet($customAlphabet);
    

    Here's the output samples

    SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
    q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
    I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
    AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
    duoRF6gAawNOEQRICnOUNYmStWmOpEgS
    sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
    eVywMdYXczuZmHaJ50nIVQjOidEVkVna
    baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
    iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
    OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa
    

    I hope it will help someone. Cheers!

    0 讨论(0)
  • 2020-11-21 23:12

    PHP 7 standard library provides the random_bytes($length) function that generate cryptographically secure pseudo-random bytes.

    Example:

    $bytes = random_bytes(20);
    var_dump(bin2hex($bytes));
    

    The above example will output something similar to:

    string(40) "5fe69c95ed70a9869d9f9af7d8400a6673bb9ce9"
    

    More info: http://php.net/manual/en/function.random-bytes.php

    PHP 5 (outdated)

    I was just looking into how to solve this same problem, but I also want my function to create a token that can be used for password retrieval as well. This means that I need to limit the ability of the token to be guessed. Because uniqid is based on the time, and according to php.net "the return value is little different from microtime()", uniqid does not meet the criteria. PHP recommends using openssl_random_pseudo_bytes() instead to generate cryptographically secure tokens.

    A quick, short and to the point answer is:

    bin2hex(openssl_random_pseudo_bytes($bytes))
    

    which will generate a random string of alphanumeric characters of length = $bytes * 2. Unfortunately this only has an alphabet of [a-f][0-9], but it works.


    Below is the strongest function I could make that satisfies the criteria (This is an implemented version of Erik's answer).
    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) works as a drop in replacement for rand() or mt_rand. It uses openssl_random_pseudo_bytes to help create a random number between $min and $max.

    getToken($length) creates an alphabet to use within the token and then creates a string of length $length.

    Source: http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322

    0 讨论(0)
提交回复
热议问题