Security Tip: Increase Your bcrypt Rounds

[Tip#58] It's time to upgrade your bcrypt rounds to 12 (or higher)!

Security Tip: Increase Your bcrypt Rounds
I’ve provided some background, skip down if you just want the summary!

bcrypt is a password hashing function used for protecting passwords within databases so they cannot be identified and used by someone who gains access to the raw hash. It is currently the default hashing algorithm within both Laravel and PHP itself, and at the time of writing, is the most secure option we have for hashing passwords4.

There is occasionally talk of switching to Argon2, and it is often promoted as a better alternative to bcrypt, however there are some fundamental flaws in Argon2 that make it unsuitable for web applications. This question was raised in my PR and on the PHP internals mailing list, and one of the experts involved in choosing Argon2 has publicly said it was a mistake to select it.

As a super quick overview, password hashing is used to protect passwords when stored on the server or in a database. When the newly created password is submitted, it is passed into the hashing function and the resulting hash is stored. Since this hash is a one-way operation, there is no way to extract the hashed password. When the user tries to login or a password needs to be checked, the raw value of the password - from user submission - is hashed and the two hashes are compared. If the hashes are the same, then the password must be the same.

Although there is no way to directly extract a hashed password, it is possible to guess hashed passwords through brute force, by generating millions/billions of passwords per second and comparing them to the hash. This process can crack simple/known passwords in seconds. More complicated passwords take more time, but as computing power increases, the time required to crack passwords goes down.

Therefore, as computing power increases, we need to add more security into our password hashing. This is done through the Rounds / Costs / Work Factor, which effectively slows down the hashing function. Therefore, if it takes longer to generate a password hash, then it takes longer to brute force password hashes, and for a sufficiently complex password this time to brute force becomes unfeasible in any realistic scenario.

PHP’s support of password hashing with bcrypt was introduced 11 years ago and started with the default of 10. Likewise, Laravel defaulted to bcrypt and 10 rounds. However as I just mentioned, computing power is always increasing and 10 rounds is no longer considered sufficient for password hashes. As such, PHP has an open RFC to upgrade the default from 10 to 11 or 12, and to follow their lead, we’ve done in the same in Laravel by bumping the default to 12.

UPDATE: The RFC passed and 12 was chosen as the new default rounds in PHP 8.4!

So… all of that to say we’ve increased the default bcrypt rounds from 10 to 12!

Laravel skeleton PR:
Laravel framework PR:

Since this value is defined in the config/hashing.php file, you need to find the bcrypt.rounds value and change it to 12 from 10.

'bcrypt' => [
    'rounds' => env('BCRYPT_ROUNDS', 12),

That’s it! 🙂

Password hashes are backwards compatible and auth systems should be configured to automatically rehash passwords when they encounter older hashes, so there is no risk of anything breaking.

So just a bit more theory: We’ve picked 12 because the benchmarks indicate the hash generation is still well under the 500ms point, at which point requests involving hashing may be noticeably slow and system performance may be affected. However, it’s worth pointing out that if you require a high level of security or have lots of resources available, you could increase your rounds to 13+. Just monitor the performance impacts and choose a level that works for you.

UPDATE: To clarify the question of “why now?”, the simple fact is that we need to be proactive about security. As computing increases, hash times decrease, and the easier it becomes to brute-force hashes. OWASP recommends “As a general rule, calculating a hash should take less than one second.”. Combine that with simple benchmarks that show 10 rounds taking less than 0.05 seconds to execute, compared with 12 sitting around 0.2-0.3 seconds and the security benefit become clear.

Looking to dive deeper into Laravel security? Check out Practical Laravel Security, my hands-on security course that uses interactive hacking challenges to teach you about how vulnerabilities work, so you can avoid them in your own code!
When was your last security audit or penetration test? Book in a Laravel Security Audit and Penetration Test today!