Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How safe is it to use phpass between several servers?

With 'portable_hashes' turned on. I've noticed that for whatever reason, the hashes it generates aren't always the same - but always return as valid when passed through 'CheckPassword'. I've also noticed that 'PHP_VERSION' is used in the generation of the hash - these two things combined have me worried... How portable is portable? Can I move the hashes (Saved in a user database) between servers, linux, windows, 64-bit, 32-bit, etc. - and still have them validate? What would I have to do to make the passwords not validate anymore?

The reason I ask is because I'm using phpass for passwords in my framework which will power several of my sites, many of which currently have several thousands of users - and there have been cases where I've had to move them onto different servers, and of course upgrade php. I also may switch one or two of them off of Apache to, say, lighthttpd or something similar. Needless to say I'm extremely paranoid I'm going to have a support nightmare someday and I won't be able to fix it in any other way than emailing new passwords to everyone (Which sounds really insecure).

If there's even the slightest chance that the passwords will ever be made invalid - what steps would I have to take to make my own password hash generator? I already use a 16-byte random salt (Per-user), and other than that the only other issue is stretching - right?

like image 521
Jon Avatar asked Jan 20 '23 16:01

Jon


2 Answers

Depending on the PHP version, you do not need to have portable hashes on. On PHP 5.3 and above, PHP supplies its own implementation of bcrypt if it isn't available on the system. If all your servers have PHP 5.3 and above, I highly recommend to turn portable hashes off. PHPass "portables hashes" exists because, depending of the version of PHP installed, bcrypt might not be available.

That said, PHPass portable hashes does store the salt in its hash. That's why every run on the same password is different.

Also, PHPass uses PHP_VERSION during the generation of those hashes* to check if the md5() function available with that version supports the $rawMode parameter. If it doesn't, pack() is use to transform the hexadecimal data into binary (note that this is considerably slower then simply using $rawMode, which is why the branch is made).

Again, if all your servers are running PHP 5.3 and above, I highly recommend to turn off portable mode and let PHPass use bcrypt instead. Since PHP 5.3+ provides its own implementation when the system one isn't available, your hash will be checkable across OSes. Even if you do turn off portable mode, PHPass will still be smart enough to check your old hashes the proper way.

I was in the same situation as you, using PHPass in my framework across multiple sites. Since I turned off portable mode, I've set my login script to progressively re-hash passwords which are not using bcrypt on login.

* Line 131


EDIT: For more explanation, here is how hashes in portable mode are generated (simplified, does not use actual variables found in PHPass, but accurate). Note that PHPass uses their own version of base64 encoding.

  1. $final = '$P$'

  2. $final .= encode64_int($rounds) (from constructor, minimum is 5 on PHP 5+, 3 other)

  3. $final .= genSalt() (Salt is 6 bytes... 8 bytes in "encode64" format).

  4. $hash = md5($salt . $password)

  5. For 2$rounds times, do $hash = md5($hash . $password)

  6. $final = encode64($hash)

So the final hash essentially is this:

$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0
\__________/\____________________/
  \                   \
   \                   \ Actual Hash
    \
     \  $P$   9   IQRaTwmf
        \_/   \   \______/
         \     \      \
          \     \      \ Salt
           \     \ 
            \     \ # Rounds (not decimal representation, 9 is actually 11)
             \
              \ Hash Header
like image 146
Andrew Moore Avatar answered Jan 30 '23 00:01

Andrew Moore


The only use that I can see of PHP_VERSION is in this line:

$output .= $this->itoa64[min($this->iteration_count_log2 +
    ((PHP_VERSION >= '5') ? 5 : 3), 30)];

Now, all that's saying is determining the maximum number of iterations. And it's in the gensalt_private method which generates salts. So this would only happen when storing a new password and generating the salt. So all previously generated salts are 100% portable. So there's no real portability issue with that at all...

As far as the rest, as long as you're using a reasonably recent version of php (5.0+), you shouldn't have any portability problem at all as far as I can tell (since the hash function is built in)...

like image 22
ircmaxell Avatar answered Jan 30 '23 01:01

ircmaxell