72

What is the best way of generating a hash for the purpose of storing a session? I am looking for a lightweight, portable solution.

Gajus
  • 69,002
  • 70
  • 275
  • 438
Eric Gates
  • 897
  • 1
  • 9
  • 7
  • If you want secure random strings, use something like [`Random::alphanumericString($length)`](https://github.com/delight-im/PHP-Random). This is a random string, not a hash. If you want a hash, PHP has several algorithms included, but you didn’t specify what you want to calculate a hash from. How to integrate this into session management is a different question. – caw Nov 20 '19 at 16:32

9 Answers9

77
bin2hex(mcrypt_create_iv(22, MCRYPT_DEV_URANDOM));
  1. mcrypt_create_iv will give you a random sequence of bytes.
  2. bin2hex will convert it to ASCII text

Example output:

d2c63a605ae27c13e43e26fe2c97a36c4556846dd3ef

Bare in mind that "best" is a relative term. You have a tradeoff to make between security, uniqueness and speed. The above example is good for 99% of the cases, though if you are dealing with a particularly sensitive data, you might want to read about the difference between MCRYPT_DEV_URANDOM and MCRYPT_DEV_RANDOM.

Finally, there is a RandomLib "for generating random numbers and strings of various strengths".

Notice that so far I have assumed that you are looking to generate a random string, which is not the same as deriving a hash from a value. For the latter, refer to password_hash.

Community
  • 1
  • 1
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • 27
    Just a little PSA: `mcrypt_create_iv` was deprecated in PHP 7.1.0. Use [`random_bytes`](http://uk1.php.net/manual/en/function.random-bytes.php) instead. – Super Cat Feb 04 '17 at 04:05
  • 7
    Removed in PHP 7.2.0: https://secure.php.net/manual/en/intro.mcrypt.php – 0b10011 Mar 15 '18 at 16:54
47

random_bytes() is available as of PHP 7.0 (or use this polyfill for 5.2 through 5.6). It is cryptographically secure (compared to rand() which is not) and can be used in conjunction with bin2hex(), base64_encode(), or any other function that converts binary to a string that's safe for your use case.

As a hexadecimal string

bin2hex() will result in a hexadecimal string that's twice as many characters as the number of random bytes (each hex character represents 4 bits while there are 8 bits in a byte). It will only include characters from abcdef0123456789 and the length will always be an increment of 2 (regex: /^([a-f0-9]{2})*$/).

$random_hex = bin2hex(random_bytes(18));
echo serialize($random_hex);

s:36:"ee438d1d108bd818aa0d525602340e5d7036";

As a base64 string

base64_encode() will result in a string that's about 33% longer than the number of random bytes (each base64 character represents 6 bits while there are 8 bits in a byte). It's length will always be an increment of 4, with = used to pad the end of the string and characters from the following list used to encode the data (excluding whitespace that I added for readability):

abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
/+

To take full advantage of the space available, it's best to provide an increment of 3 to random_bytes(). The resulting string will match /^([a-zA-Z\/+=]{4})*$/, although = can only appear at the end as = or == and only when a number that is not an increment of 3 is provided to random_bytes().

$random_base64 = base64_encode(random_bytes(18));
echo serialize($random_base64);

s:24:"ttYDDiGPV5K0MXbcfeqAGniH";

0b10011
  • 18,397
  • 4
  • 65
  • 86
  • Does this method have any advantages over creating a string that uses extra special characters. I use a method randomly generates same length but with larger character combination that have numbers, uppercase, lowercase and specials characters. Which mathematically looks more secure. – Zortext Sep 21 '20 at 10:55
  • 3
    @Zortext `random_bytes()` is built into PHP (so could be faster than anything you create) and can be trusted to be cryptographically secure (compared to `rand()` which is **not**). As for `bin2hex()`, you could convert the binary to something else without too much concern of affecting security (it'll just be more time consuming, but could save a few bytes for the end user). The easiest would probably be `base64_encode()` which would only ~33% be larger than the binary input. Alternatively, you could use `random_int()` in a loop that maps to an array of characters. – 0b10011 Sep 27 '20 at 20:24
  • 2
    @Zortext If your concern is security of the session, increase `16` to a number that makes you feel better. 16 bytes is 32 hex characters which means there are `16^32` different possibilities, or 3.4e38 possibilities. So I'd recommend figuring out what the attack surface you're trying to prevent looks like and adjust the number of bytes accordingly. If your concern is storage used by the cookie/session, use `base64_encode()`, or roll your own using `random_int()` and an array of more than 64 characters. – 0b10011 Sep 27 '20 at 20:30
  • what is the `serialize` for? – Merc Feb 06 '23 at 22:23
  • 1
    @Merc The `serialize()` calls are not necessary and are only to illustrate that the output is a string and how many characters each string is in comparison to the number of random bytes generated. Both use 18 random bytes, but `bin2hex()` builds a 32-character string while `base64_encode()` makes a 24-character string. As far as what you should use: you only _need_ `bin2hex(random_bytes(18))` or `base64_encode(random_bytes(18))`; everything else is extra. – 0b10011 Feb 15 '23 at 16:02
14

You can use PHP's built-in hashing functions, sha1 and md5. Choose one, not both.

One may think that using both, sha1(md5($pass)) would be a solution. Using both does not make your password more secure, its causes redundant data and does not make much sense.

Take a look at PHP Security Consortium: Password Hashing they give a good article with weaknesses and improving security with hashing.

Nonce stands for "numbers used once". They are used on requests to prevent unauthorized access, they send a secret key and check the key each time your code is used.

You can check out more at PHP NONCE Library from FullThrottle Development

Anthony Forloney
  • 90,123
  • 14
  • 117
  • 115
  • Thanks. Im going to look into it – Eric Gates Feb 19 '10 at 02:44
  • 5
    SHA1 can be broken as well (at least theoretically). See [Hash function security summary](https://en.wikipedia.org/wiki/Hash_function_security_summary) for details. I'd go with SHA256 or SHA512. – colan Nov 27 '15 at 00:09
  • 8
    Very old thread, but SHA1 is now officially broken! http://www.theverge.com/2017/2/23/14712118/google-sha1-collision-broken-web-encryption-shattered – SuperNOVA Feb 28 '17 at 04:08
12

Maybe uniqid() is what you need?

uniqid — Generate a unique ID

z-boss
  • 17,111
  • 12
  • 49
  • 81
8

You can use openssl_random_pseudo_bytes since php 5.3.0 to generate a pseudo random string of bytes. You can use this function and convert it in some way to string using one of these methods:

$bytes = openssl_random_pseudo_bytes(32);
$hash = base64_encode($bytes);

or

$bytes = openssl_random_pseudo_bytes(32);
$hash = bin2hex($bytes);

The first one will generate the shortest string, with numbers, lowercase, uppercase and some special characters (=, +, /). The second alternative will generate hexadecimal numbers (0-9, a-f)

Simon Backx
  • 1,282
  • 14
  • 16
5

Use random_bytes() if it's available!

$length = 32;

if (function_exists("random_bytes")) {
    $bytes = random_bytes(ceil($length / 2));
    $token = substr(bin2hex($bytes), 0, $length)
}

Check it on php.net

Torxed
  • 22,866
  • 14
  • 82
  • 131
Zoltán Süle
  • 1,482
  • 19
  • 26
0

I personally use apache's mod_unique_id to generate a random unique number to store my sessions. It's really easy to use (if you use apache).

For nonce take a look here http://en.wikipedia.org/wiki/Cryptographic_nonce there's even a link to a PHP library.

t00ny
  • 1,598
  • 1
  • 11
  • 8
0

I generally dont manually manage session ids. Ive seen something along these lines recommended for mixing things up a bit before, ive never used myself so i cant attest to it being any better or worse than the default (Note this is for use with autogen not with manual management).

//md5 "emulation" using sha1
ini_set('session.hash_function', 1);
ini_set('session.hash_bits_per_character', 5);
prodigitalson
  • 60,050
  • 10
  • 100
  • 114
-3

Different people will have different best ways. But this is my way:

  1. Download this rand-hash.php file : http://bit.ly/random-string-generator
  2. include() it in the php script that you are working with. Then, simply call cc_rand() function. By default it will return a 6 characters long random string that may include a-z, A-Z, and 0-9. You can pass length to specify how many characters cc_rand() should return.

Example:

  1. cc_rand() will return something like: 4M8iro

  2. cc_rand(15) will return something similar to this: S4cDK0L34hRIqAS

Cheers!

Arif Billah
  • 157
  • 2
  • 11
  • This does not generate cryptographically secure values as it uses [`rand()`](https://secure.php.net/manual/en/function.rand.php). – 0b10011 Sep 24 '16 at 20:38
  • But... if you're not using it for cryptographically secure values, this works well. I'm just trying to create an identifier for something in my program. +1. – Jaden Baptista Aug 22 '20 at 00:25
  • link in article is broken, does not work anymore. – Bombelman Feb 24 '23 at 03:44