Skip to content

Instantly share code, notes, and snippets.

@nikic
Created September 12, 2012 15:04
Show Gist options
  • Save nikic/3707231 to your computer and use it in GitHub Desktop.
Save nikic/3707231 to your computer and use it in GitHub Desktop.
The new Secure Password Hashing API in PHP 5.5

The new Secure Password Hashing API in PHP 5.5

The RFC for a new simple to use password hashing API has just been accepted for PHP 5.5. As the RFC itself is rather technical and most of the sample codes are something you should not use, I want to give a very quick overview of the new API:

Why do we need a new API?

Everybody knows that you should be hashing their passwords using bcrypt, but still a surprising number of developers uses insecure md5 or sha1 hashes (just look at the recent password leaks). One of the reasons for this is that the crypt() API is ridiculously hard to use and very prone to programming mistakes.

By adding a new, very simple to use API we hope to move more developers towards bcrypt.

How to hash passwords

Creating password hashes can't be any simpler than this:

$hash = password_hash($password, PASSWORD_DEFAULT);

This will create a password hash using the default algorithm (currently bcrypt), the default load factor (currently 10) and an automatically generated salt. The used algorithm and salt will also be part of the resulting hash, so you don't need to worry about them at all ;)

If you don't want to stick with the defaults (which might change in the future), you can also provide algorithm and load factor yourself:

$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);

Verifying passwords

Verifying passwords is just as easy:

<?php
// $password from user, $hash from database
if (password_verify($password, $hash)) {
    // password valid!
} else {
    // wrong password :(
}

Remember: The salt and algorithm are part of the hash, so you don't need to provide them separately.

Rehashing passwords

As time goes by you might want to change the password hashing algorithm or load factor, or PHP may change the defaults to be more secure. In this case new accounts should be created using the new options and existing passwords rehashed on login (you can do this only on login because you need the original password to do a rehash).

Doing this is also very simple:

<?php
function password_verify_with_rehash($password, $hash) {
    if (!password_verify($password, $hash)) {
        return false;
    }

    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $hash = password_hash($password, PASSWORD_DEFAULT);

        // update hash in database
    }

    return true;
}

The above snippet will keep your hashes up to date with the PHP default. But once again you can also specify custom options, e.g. password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12']).

Compatibility layer for older PHP versions

The new API will only be introduced in PHP 5.5, but you can already use a PHP implementation of the same API now! The compatibility implementation will automatically disable itself once you upgrade to 5.5.

@nikic
Copy link
Author

nikic commented Sep 12, 2012

@squeed: The password_verify implementation performs a constant-time comparison to avoid timing attacks ;) So no need to worry about that :)

@squeed
Copy link

squeed commented Sep 12, 2012

@nikic: So it does :-)

@mattvh
Copy link

mattvh commented Sep 12, 2012

@dserodio: I don't think so. It hashes quickly on my VPS (a two-node VM from VPS.net) and even if it takes 0.6-1 seconds for a a cheap shared host to do it, it's not like a hash needs to be computed terribly often. A slow loading time when they first log in is perfectly acceptably, and the host isn't going to kill the script unless it runs really long (like 30-60 seconds, then it usually times out on most shared plans). It's not like you're hitting bcrypt on every page load...

@stouset
Copy link

stouset commented Sep 13, 2012

The responses to this so far are pretty terrifying. Here PHP is doing the absolutely right thing out of the box, and already users are discussing ways to circumvent it according to their own intuition.

@g1o5b: This isn't any more secure…

Yes, it is. While one is certainly capable of using the existing APIs correctly, this uses cryptographic best practices by default. The old way makes it trivial to shoot yourself in the foot. This way makes it trivial to do things correctly.

@benwerd: Can you specify a salt if you want to? I'm not sure how I feel about purely-invisible salts.

The salt is prepended to the digest. There is no issue whatoever with doing this, and in all likelihood the average person will get salt generation wrong.

@sparticvs: You should just use: http://php.net/manual/en/function.hash-pbkdf2.php as that is the same thing and native and well, better because you can pick your algo.

No. PBKDF2 is a workaround to make fast message digest algorithms slower. BCrypt has this built-in as a core concept, and is intended specifically for this purpose. Picking your own algorithm is exactly why so many real-world password hashing schemes are broken: people are still writing new software that digests passwords with MD5. This does the correct thing out of the box.

@squeed: Watch out though: PHP's string comparison functions are very susceptible to timing attacks!

First, this is why there is a password_verify function: to take this responsibility out of your hands. Still, though, digests output from BCrypt can generally be compared with each other using standard string comparison functions safely, because an attacker has no practical way to input strings that hash to single-character differences.

@wyred
Copy link

wyred commented Sep 13, 2012

+1

@reconbot
Copy link

+1

@hinzundcode
Copy link

+1 ;)

Copy link

ghost commented Feb 13, 2013

I have a login / registration form in the works that uses this API. Thanks to Panique's original project, I was able to do this quicker.

http://projects.mrxxiv.com/php55login/

@hobnob
Copy link

hobnob commented Jun 20, 2013

👍

@panique
Copy link

panique commented May 20, 2014

@MRXXIV Thanks :)

@guischumacher
Copy link

hello, first of all, nice post! My question is: now that we have a specific function for password hash, the best method for verifying if the user is logged in some system is still by using sessions? or there's a better way on some new 5.5 functions? Thanks in advance!

@strapin3
Copy link

strapin3 commented Jan 5, 2016

is PHP 5.5 Password Hashing API available for free?

@peter279k
Copy link

Hi @strapin3. Yes, it's available for free and you can see this link to know more details.

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