Skip to content

Instantly share code, notes, and snippets.

@raveren
Last active November 21, 2023 12:35
Show Gist options
  • Star 94 You must be signed in to star a gist
  • Fork 18 You must be signed in to fork a gist
  • Save raveren/5555297 to your computer and use it in GitHub Desktop.
Save raveren/5555297 to your computer and use it in GitHub Desktop.
Generate cryptographically secure random strings. Based on Kohana's Text::random() method and this answer: http://stackoverflow.com/a/13733588/179104
function random_text( $type = 'alnum', $length = 8 )
{
switch ( $type ) {
case 'alnum':
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'alpha':
$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'hexdec':
$pool = '0123456789abcdef';
break;
case 'numeric':
$pool = '0123456789';
break;
case 'nozero':
$pool = '123456789';
break;
case 'distinct':
$pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
break;
default:
$pool = (string) $type;
break;
}
$crypto_rand_secure = function ( $min, $max ) {
$range = $max - $min;
if ( $range < 0 ) return $min; // not so random...
$log = 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;
};
$token = "";
$max = strlen( $pool );
for ( $i = 0; $i < $length; $i++ ) {
$token .= $pool[$crypto_rand_secure( 0, $max )];
}
return $token;
}
@firehawk895
Copy link

Thanks, This is very neat :)

@jamesearlywine
Copy link

I love this. Thank you. :)

@rad8329
Copy link

rad8329 commented May 5, 2016

Thanks, simple and efficient :)

@rakib10rr3
Copy link

rakib10rr3 commented Dec 26, 2017

Will this produce unique value every time?
i know random, but will this be unique?

Copy link

ghost commented May 2, 2018

This code uses recommended methods like openssl and in my opinion is good. However You can see my implementation is very wide and generate passwords in all languages.

@cocest
Copy link

cocest commented Oct 2, 2019

What can I say? Thank you.

@captn3m0
Copy link

Crypto Warning

openssl_random_pseudo_bytes can return false, and this is not being considered here. If you are using modern PHP, just use random_int instead.

Returns the generated string of bytes on success, or FALSE on failure.

@raveren
Copy link
Author

raveren commented Dec 15, 2019

@catn3m0 this is used in multiple production environments for years and has never misbehaved - nor has been reported as such.

The false on failure can be caused by a setup issue which you should fix with your php installation anyway.

EDIT: updated function [diff]

@captn3m0
Copy link

Thanks for upgrading to random_int. There is detailed prior discussion on this at https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php, paragonie/random_compat#5 which covers issues and fixes. To copy the summary:

Given the above, we cannot in good conscience recommend openssl_random_pseudo_bytes() as a CSPRNG. If you can use an alternative to openssl_random_pseudo_bytes(), please do so.

Look at the polyfill for a list of issues that need to be tackled for a secure CSPRNG in PHP. The current version refuses to fallback to OpenSSL.

It is much more tricker than the implementation here. People should just use https://github.com/paragonie/random_compat

@ph33nx
Copy link

ph33nx commented Dec 18, 2019

1 out of 5-6 times, php throws this error:
Notice: Uninitialized string offset: 62

My fix was to remove the check for 'random_int'

if (function_exists('random_int')) {
		$crypto_rand_secure = 'random_int';
} else { }

& just using the

$crypto_rand_secure = function ( $min, $max ) {
*******************************************

@raveren
Copy link
Author

raveren commented Dec 18, 2019

UPDATE: reverted to the age-old, tested function, seriously just go and use https://github.com/paragonie/random_compat

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment