18

What's the best way to generate a cryptographically secure 32 bytes salt in PHP, without depending on libraries seldom included in typical PHP installations?

After some googling I discovered that mt_rand is not considered secure enough, but I haven't found a suggestion for a replacement. One article suggested reading from /dev/random but not only this won't work on windows; it is also very slow.

I want a reasonable balance between security and speed (ie, it shouldn't take 20 seconds to generate 512 bytes, like /dev/random usually does)

qster
  • 1,129
  • 3
  • 9
  • 11
  • 2
    `/dev/urandom` can be used for faster generation, but still won't work on Windows systems. – Amber Mar 25 '10 at 07:37

8 Answers8

25

This is easier in PHP 7: Just use $salt = random_bytes($numberOfDesiredBytes); to generate a salt.

What do you need a salt for, anyway? If it's for passwords, just use password_hash() and password_verify().

Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
  • 1
    Thanks for the note about `password_hash`. Turn out it works better for me than the original solution. –  Oct 12 '19 at 12:47
10

Note: mcrypt has been deprecated in PHP 7.1. Skip to the up-to-date answer.

You might want to take a look at the documentation (and comments) for mcrypt_create_iv().

Community
  • 1
  • 1
Amber
  • 507,862
  • 82
  • 626
  • 550
  • 2
    mcrypt is deprecated in PHP 7.1 . You shouldn't write code that is already deprecated. So if you read this in 2016, don't use this answer, prefer @Scott Arciszewski 's one. – JesusTheHun Nov 30 '16 at 13:39
  • mcrypt_create_iv() will be deprecated so better to use random_bytes() as suggested here - http://us.php.net/manual/en/function.mcrypt-create-iv.php – Kamal Joshi Feb 08 '19 at 21:38
9

Note: mcrypt has been deprecated in PHP 7.1. Skip to the up-to-date answer.

You can use the function mycrypt_create_iv(), since PHP Version 5.3 it also uses the random source on a Windows server (not only on Unix). Before using it, you should check if the constant MCRYPT_DEV_URANDOM is defined.

mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);

Unlike random, urandom does not block the server, if there is not enough entropy available. Since the password salt should be unique (not necessarily random), urandom seems to be a good choice to me.

Community
  • 1
  • 1
martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
  • Please note that the second argument is optional and the value `MCRYPT_DEV_URANDOM` is the default - so if you don't want to change it, you can just as well do `mcrypt_create_iv($length)`. More over, this feature is implemented in the `mcrypt` extension which may or may not be installed on your server. Lastly, the result of that function is a binary string which is often not what you need, as you want to save the salt for referencing later. I usually `base64_encode` the result before using it as a salt. – Guss Jan 12 '16 at 17:50
  • @Guss - These are good points. Moreover one needs to check, in what form the salt is expected. BCrypt for example will not accept a binary salt, but a base64 encoded salt isn't valid neither. For password hashing it is best to use the built in function [password_hash()](http://www.php.net/manual/en/function.password-hash.php), which generates a safe salt on its own. – martinstoeckli Jan 13 '16 at 06:33
  • One reason not to use `password_hash()` is that I'm not implementing a password. I need a salt to implement a stateless "api key" style authorization protocol. – Guss Jan 13 '16 at 07:06
  • In addition, password_hash() is not available on all servers. My server, for example, is stuck on php 5.3. password_hash() is not available until 5.5. – RoboticRenaissance May 18 '16 at 19:08
  • @RoboticRenaissance - This should not be a problem, there is a [compatibility pack](https://github.com/ircmaxell/password_compat/blob/master/lib/password.php) for 5.3.7 and later. With a minor tweak it can be used even with 5.3. – martinstoeckli May 19 '16 at 07:37
3

uniqueid is not well suited for generating a random string as it too is microtime based. A CPU Cycle is generally much shorter than a microtime-tick, which may lead to possible constancy for a given variable within loops. Setting the second parameter "entropy" to true,

 uniqid('', true)

will provide increased randomness.

To get a random string that is well compatible with most character-sets,one may apply base64 encoding to the mcrypt initilization vector function mcrypt_create_iv:

$length = 16;
base64_encode(mcrypt_create_iv(ceil(0.75*$length), MCRYPT_DEV_URANDOM))
//> hlZuRJypdHFQPtI2oSFrgA==
strlen(base64_encode(mcrypt_create_iv(ceil(0.75*$length), MCRYPT_DEV_URANDOM)))
//> 16

Reducing the character-alphabet to 2^6Bit increases the size, which is accounted for above.

Lorenz Lo Sauer
  • 23,698
  • 16
  • 85
  • 87
1

Read from /dev/urandom, or use openssl_random_pseudo_bytes().

D.W.
  • 3,382
  • 7
  • 44
  • 110
0

Looks like this question has an accepted answer but I just want to additionally state after a bit of research and reading this thread that you might add some security if you don't put all your eggs in one basket.

I might suggest not relying solely on PHP to create your salts and hashing your passwords. You can obfuscate your solution a little more if you let the database do part of the work.

Someone suggested just using password_hash() and password_verify(). While those are great methods, I strongly recommend sticking to the idea of incorporating a salt in addition to these.

To answer the question, a salt can be anything that is truly random and enforced as unique to the user. You can technically generate it however you wish as long as you adhere to those 2 rules.

a couple of good resources:

https://www.codeproject.com/Articles/704865/Salted-Password-Hashing-Doing-it-Right https://auth0.com/blog/adding-salt-to-hashing-a-better-way-to-store-passwords/

Justin Grant
  • 245
  • 3
  • 10
-2

uniqid() should be fine for this purpose.

Crozin
  • 43,890
  • 13
  • 88
  • 135
  • 1
    I use uniqid() with some fast computable seed value (like the suggested microtime), too. Haven't done a cryptographic analysis, but seems to fit the bill. – Boldewyn Mar 25 '10 at 08:15
-7

I think microtime() is enough.

Strangely, but I am still getting downvotes for this answer.

Though the only explanation I get is that microtime is predictable.
It sounds strange to me as salt always assumed as openly known - so, there is no use for prediction at all.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • 1
    Not really. Not only it's very very easy to predict (it's the current time), I also can't get 32 bytes out of it. – qster Mar 25 '10 at 07:37
  • 4
    @qster you can use it as random number generator. And get any number of bytes you want. And it is not the current time, you messed it up with time(). Microseconds do change pretty fast. And it's only salt, you don't need too much security on salt. – Your Common Sense Mar 25 '10 at 07:46
  • @YourCommonSense Run `echo microtime()` through a for loop (10 loops is fine) and see how wrong you are... – Mike S Nov 29 '12 at 15:39
  • Strangely, but some comments may contain a contradiction even in a single line: "it's current time[stamp] but I can't get 32 bytes out of it". I've always thought that unix timestamp is a 32-bit number exactly. – Your Common Sense Aug 03 '13 at 07:40
  • 4
    @YourCommonSense: bits are not bytes. – datashaman Oct 17 '13 at 06:57