Let me begin this post by saying that almost no event is truly random. Even the outcome of a classic coin toss could in theory be predicted if we knew the effect of every factor involved, like air friction, gravity, and initial force.
The same thing is applicable to the generation of random numbers and alphanumeric strings. The best we can hope for is to generate numbers and strings that don't seem to follow a pattern and can't be practically predicted by an attacker.
In this tutorial, we will cover different techniques for generating random numbers and alphanumeric strings in PHP. Some of them will be cryptographically secure, while others are meant only for casual use, like assigning pseudo-random file names or creating URLs and suggesting usernames.
Generating Random Numbers in PHP
There are three different functions for generating random numbers in PHP. All of them will accept a minimum and maximum possible value for the random numbers and output a random number for you. These are rand($min, $max)
, mt_rand($min, $max)
, and random_int($min, $max)
.
With rand()
, the minimum and maximum values of integers you can generate lie between 0
and the value returned by getrandmax()
. Before PHP 7.1.0, this function was about four times slower than mt_rand()
. However, starting from PHP 7.1.0, it has been made an alias of mt_rand()
. Unlike mt_rand()
, though, you can set the value of $max
to be lower than $min
without causing an error.
With mt_rand()
, the minimum and maximum values of integers you can generate lie between 0
and the value returned by mt_getrandmax()
. It relies on an implementation of the Mersenne Twister to generate random numbers. Watch out, though—prior to PHP 7.1.0, this function implemented an incorrect version of the algorithm to generate the numbers. However, it has been fixed in newer versions.
The function became even better in PHP 7.2.0 by getting rid of a modulo bias bug. This means that for some particular seeds, your sequence of random numbers will now be slightly better compared to older versions. Some specialized code might actually rely on this bias, though. If so, you can use the older seed algorithm by calling the mt_srand()
function to seed the random number generator and passing MT_RAND_PHP
as the value of the second parameter.
The mt_rand()
function has a period of 219937−1, which basically means that in best case scenarios you get as many as 219937−1 random numbers before the sequence starts repeating. You should note that repetition of a sequence is not the same as repetition of a particular number. In other words, you might get the same random number twice, but that does not mean that the sequence itself has started repeating. The following sequence is an example:
187 3276 1267 15 1267 34598 3467 125 17
In the above sequence, we had 1267 twice in the output, but that does not mean that the whole sequence started repeating after that. It's unlikely to get the same number repeated so soon in a random sequence, but it is possible!
Cryptographically Secure Random Integers
If you want cryptographically secure pseudo-random numbers, the random_int()
function in PHP is your best bet. It will generate random numbers between the provided $min
and $max
values, which default to PHP_INT_MIN
and PHP_INT_MAX
. Unfortunately, this function is only available starting from PHP 7.0. For versions before that, you can use this polyfill on GitHub.
Random Floats
Instead of generating random integers, you might also want to generate floats. This can be done effortlessly by simply dividing a random number with a value returned by mt_getrandmax()
. The following example will illustrate how to generate a random float between 0 and 1 or between any other minimum and maximum limits.
<?php // Output: 0.69458310943776 echo mt_rand(0, mt_getrandmax())/mt_getrandmax(); function mt_random_float($min, $max) { $float_part = mt_rand(0, mt_getrandmax())/mt_getrandmax(); $integer_part = mt_rand($min, $max - 1); return $integer_part + $float_part; } // Output: 10.199064863938 echo mt_random_float(10, 11); // Output: 35.540808309121 echo mt_random_float(15, 50); ?>
When generating a random float between given limits, we make sure that the random integer numbers do not go above $max - 1
. This way, we can be sure that adding the float part will not take the number above the maximum limit.
Seeding the Random Number Generators
One concept that needs a little bit of explanation is seeds. Put simply, these are just numbers that can be used to initialize the rand()
and mt_rand()
functions before generating any random numbers. The function which seeds rand()
is called srand($seed)
, and the function which seeds mt_rand()
is called mt_srand($seed, $mode)
.
It's important to remember that providing an initial seed value every single time before calling rand()
and mt_rand()
won't necessarily produce better random numbers. In fact, using the same seed each time will give you the same random number as well!
<?php mt_srand(10); // Output: 1656398468 echo mt_rand(); mt_srand(10); // Output: 1656398468 echo mt_rand(); mt_srand(10); // Output: 1656398468 echo mt_rand(); ?>
Seeding a random number is useful in situations where you want to generate a random but reproducible sequence. The following code snippet generates the same sequence of random numbers when run twice.
<?php mt_srand(10); $count = 0; while($count < 10) { echo mt_rand(0, 100)." "; $count++; } // Output on First Run: // 68 58 68 13 3 48 30 37 96 82 // Output on Second Run: // 68 58 68 13 3 48 30 37 96 82
Generating reproducible random sequences this way can help debug programs which were being tested using random data—if you keep track of the seed, you can reproduce the same input to figure out what went wrong.
Generating Random Alphanumeric Strings in PHP
There are many ways to generate random alphanumeric strings, and what you use will depend on your needs.
Generate Shuffled Strings
If you want to generate random alphanumeric strings from a fixed set of characters, you can use the str_shuffle($string)
function. This function will provide you a randomly shuffled string. Starting from PHP 7.1, the algorithm which determines the random order of characters in the output string has been changed to the Mersenne Twister.
Remember that the random string generated this way is not cryptographically secure. However, the string will still be pretty unpredictable for common use like generating random file names or URLs. Here are a few examples:
<?php $permitted_chars = '0123456789abcdefghijklmnopqrstuvwxyz'; // Output: 54esmdr0qf echo substr(str_shuffle($permitted_chars), 0, 10); $permitted_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; // Output: video-g6swmAP8X5VG4jCi.mp4 echo 'video-'.substr(str_shuffle($permitted_chars), 0, 16).'.mp4'; ?>
Your output will most probably be different in both cases. In the first case, we just shuffled the permitted characters string and then took the first 10 characters of it. In the second case, we added "video" at the beginning of the generated string and ".mp4" at the end.
This method of generating random alphanumeric strings is very easy, but it has a couple of issues. For example, you will never get the same characters in your random string twice. Also, the length of the random output string can only be as long as the input string.
Generate Random Strings
If the problems I listed above are a deal breaker, you might want to look at some other implementations. The following code will solve both these problems.
<?php $permitted_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; function generate_string($input, $strength = 16) { $input_length = strlen($input); $random_string = ''; for($i = 0; $i < $strength; $i++) { $random_character = $input[mt_rand(0, $input_length - 1)]; $random_string .= $random_character; } return $random_string; } // Output: iNCHNGzByPjhApvn7XBD echo generate_string($permitted_chars, 20); // Output: 70Fmr9mOlGID7OhtTbyj echo generate_string($permitted_chars, 20); // Output: Jp8iVNhZXhUdSlPi1sMNF7hOfmEWYl2UIMO9YqA4faJmS52iXdtlA3YyCfSlAbLYzjr0mzCWWQ7M8AgqDn2aumHoamsUtjZNhBfU echo generate_string($permitted_chars, 100); ?>
You can modify it to add particular suffixes and prefixes to the generated random string. People who are using PHP 7 can improve the string generation further by using the cryptographically secure function random_int()
instead of mt_rand()
.
Generate Random Hexadecimal Strings
If you want to generate random hexadecimal strings in PHP, you can also use either the md5($string, $raw_output)
or the sha1($string, $raw_output)
function. Both of them will generate hashes of a given input string.
You will keep getting unique hashes as long as the input is unique. This could be achieved by using the output of a function like time()
as the input. By default, md5()
will return a 32-character hexadecimal string, and sha1()
will return a 40-character hexadecimal string. These can be trimmed to a specific length using the substr()
function.
Here is an example of the output returned by these functions:
<?php // Output: 36e5e490f14b031e echo substr(md5(time()), 0, 16); // Output: aa88ef597c77a5b3 echo substr(sha1(time()), 0, 16); // Output: 447c13ce896b820f353bec47248675b3 echo md5(time()); // Output: 6c2cef9fe21832a232da7386e4775654b77c7797 echo sha1(time()); ?>
As you can see, generating random and unique hexadecimal strings up to 40 characters long is very easy in PHP.
Generate Cryptographically Secure Random Strings
The three functions to generate random alphanumeric strings that we have discussed so far are not cryptographically secure. Luckily, PHP also has a function called random_bytes($length)
to generate cryptographically secure pseudo-random bytes. The $length
parameter determines how long the output string should be.
Once you have the output in terms of random bytes, you can use the bin2hex()
function to convert them to hexadecimal values. This will double the length of the string.
<?php // Output: b7b33efa07915b60ad55 echo bin2hex(random_bytes(10)); // Output: a2e6cb1f25616324c8a11a2cceb0b47c590949ea echo bin2hex(random_bytes(20)); // Output: 25af3b86e11884ef5e8ef70a0ad06cba81b89ed6af3781a0 echo bin2hex(random_bytes(24)); ?>
Another function that you can use to generate cryptographically secure random bytes is openssl_random_pseudo_bytes($length, &$crypto_strong)
. The value of the second parameter can be used to determine if the output string will be generated using a cryptographically secure algorithm or not.
Final Thoughts
In this tutorial, we looked at the generation of random numbers and alphanumeric strings in PHP. Generating random numbers can be useful in a variety of situations, like in games where you have to spawn enemy players or randomly give users some clues about letters so they can form a whole word.
Just like random numbers, the generation of random alphanumeric strings can also be pretty helpful in many circumstances. With the help of str_shuffle()
, you can choose which set of characters appear in your random strings. With sha1()
and md5()
, you can easily generate random hexadecimal sequences, and with random_bytes()
you can generate cryptographically secure strings. This will allow you to generate meaningful yet randomized filenames and usernames that are hard to guess.
I hope you enjoyed this tutorial. If you have any questions, feel free to ask them in the comments.
No comments:
Post a Comment