In examining our database storage of hashed passwords with salts, I note a high rate of salt collisions, i.e., there are far too many cases where two table rows have the same salt stored.

One remediation is to immediately begin generating "better" salts as we store new salted-and-hashed passwords.

  • We run PHP 5.3 in production. That means we probably can't use libraries which say they require PHP 5.4+. (I say "probably" because our CentOS integration does include some PHP 5.4 features. We're investigating compatibility of various libraries.)

  • We do not have libsodium available at this time.

  • We do have both the mcrypt and openssl PHP extensions installed and enabled.

  • I have read through all of Scott's blog posts at Paragonie. The most relevant is How to safely generate random strings and integers in PHP.

I believe my best option for creating a password salt in these circumstances is to use mcrypt for obtaining bytes from /dev/urandom:

$salt = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)

assuming 16 bytes is appropriate for the salt. The PHP documentation does not even say if that first parameter is in bytes! But it does appear to be so.

We have a few tens of millions of users, so allowing for 100 million salts should be reasonable.

Given my constraints, can anyone suggest a better approach than taking 16 random bytes from /dev/urandom via mcrypt extension?

To be sure, we are also working on other upgrades to get us to better library availability, stronger hashing, etc. That's outside the scope of this question.

  • 2
    I honestly think you are in the right way. However, I suggest using 24 bytes instead of 16. It will add so much more possibility. Also, using slow matching functions like PBKDF2 will give you higher security. This library tells you why. – Saehun Sean Oh Sep 18 '15 at 5:09
  • 1
    If you have php 5.3.7 or above you can use the composer package 'ircmaxell/password_compat' to get php5.5's new password functions. – Maerlyn Sep 18 '15 at 5:20
  • 1
    salt = bin2hex(openssl_random_pseudo_bytes(22)); – void_in Sep 18 '15 at 5:57
  • 1: The Crack Station link is useful, thank you. 2: I did not realize ircmaxell/password_compat works with PHP 5.3. We'll give it a test. 3: I am preferring mcrypt with urandom because openssl_random_pseudo_bytes is weaker. sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers – Edward Barnard Sep 18 '15 at 11:17

So what do you want from your salts?

  1. They should be somewhat unpredictable to prevent pre-computation attacks.
  2. They should be globally unique.
  3. They should be as small as possible as it needs to highly scale.

The only assumption you need to make is that /dev/urandom is secure and at least provides statistically random numbers (which it should). This is a reasonable assumption as otherwise nearly everything (crypto related) on your system would be broken, as very most of them use /dev/urandom. It further needs to be assumed that mcrypt basically just forwards /dev/urandom's output.

To satisfy property #1 you'd need to have at least 128 bit (=16 bytes) salts, but this would leave you with a good probabilty of a collision after having observed 2^(128/2)=2^64 salts, which may be too soon for you to be really sure that they're unique. I thereby propose to use 32 byte (= 256 bit) salts that have 128 bit collision resistance making it really unlikely to ever re-use any salt and salt will still be reasonably small (you could also use 512-bit salts...).

As far as password hashing is concerned you may want to have a look at the related competition.

  • I particularly enjoyed Solar Designer's retrospective on hashing. Good link! – Edward Barnard Sep 18 '15 at 19:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.