Laravel's Bcrypt Hasher

November 30, 2016 —John Koster

The Illuminate\Hashing\Bcrypt hasher is registered with the service container in the Illuminate\Hashing\HashServiceProvider service provider, and is bound to the name hash (the hash key in the service container is itself an alias for the Illuminate\Contracts\Hashing\Hasher interface). The following code sample would return an instance of the Bcrypt hasher by default:

1<?php
2 
3// Get a new Bcrypt instance.
4$hasher = app('hash');

The Bcrypt hasher is also used by the bcrypt application helper function. It actually uses the above method to resolve the Bcrypt hasher from the service container. This means that any other service provider that modifies the hash service container entry will also affect the bcrypt helper function. The bcrypt helper function is also used internally in the Illuminate\Foundation\Auth\ResetPasswords trait; specifically, it is used in the implementation of the resetPassword($user, $password) method.

The following sections will explain the Bcrypt hasher's various methods.

#setRounds($rounds)

The setRounds method is simple way to control how many rounds, or iterations the make method will use when calculating the final hash. An argument passed to $rounds must be a positive integer between 4 and 31 (including both 4 and 31). If an integer outside the stated range is supplied an instance of ErrorException will be thrown. The internal default value for $rounds is 10.

#make($value, array $options = [])

The make method is used to hash a given $value with the provided $options. At the time of writing, the rounds value is the only option that is checked for in the $options array. See the information on the setRounds method for more information on the rounds option. The internal default value for rounds is 10.

If a user supplies a value for rounds using the $options array, that value will be used when calculating the hash. In all other scenarios, the internal value for rounds will be used (this value can be modified using the setRounds($rounds) method).

The make method internally makes a call to PHP's password_hash function with PASSWORD_BCRYPT as the argument for the $algo parameter.

The following code example will get an implementation of Illuminate\Contracts\Hashing\Hasher from the service container and generate a few hashes. The output will appear below the code example. The output will be different each time the code is ran:

1<?php
2 
3/**
4 * Get a new instance of Hahser from the service container.
5 *
6 * @var \Illuminate\Contracts\Hashing\Hasher $hasher
7 */
8$hasher = app('Illuminate\Contracts\Hashing\Hasher');
9 
10// Generate hashes for integers 0 - 9
11// using the default work factor.
12for ($i = 0; $i < 10; $i++) {
13 echo $hasher->make($i),PHP_EOL;
14}

The above would produce results similar to the following output:

1$2y$10$jVlNmKI5Gg0j.3nER7tevuUaesWYIuwoxzghpIKfb2LvNMoTGaac6
2$2y$10$DZzbYPf88wWo6nFW4LOAje4oJsWxZK.vg.k6vqxouXmb/6lsx045y
3$2y$10$7CKZ0rmSOVxjvc2XG3diLOpalxjMkjcdw1.zKeqZPiSa7dD8K7GdS
4$2y$10$qpiLO4hCLgYWmK4WJp0tTuv7klAM6RO5QrAnEqn1ULN92S.V5U4B6
5$2y$10$no8cpUgVPiLw9rYOBknKWeiO2fC45.dzzhxE.rW8qn9ZojixXvoq.
6$2y$10$31BmbMpivMGz1bsf5PAwTeEFhnS0GMS4GOkjNp.TWh9PZsZ0jLVWC
7$2y$10$76T.L071J.8ewnnI3oocoukJtC8QljZiIesebdgewyMKygsl20QU2
8$2y$10$nCE.5KTwPR8g6mrm4M9jlecoBpITCavThwRT0IZVQuMRg8qHQXaea
9$2y$10$8uQ/0uc6wB1OQ7220wA2Ze54WqMzKTxWPQyi./bODTiBL/I7QgwgW
10$2y$10$2ywQnQZKXAHMRqmj4iCr5O9Lr67Gv9u1BjXEiVspSRO.gWV1ROF7a

#check($value, $hashedValue, array $options = [])

The check method will validate a given $value against a previously generated $hashedValue. The $options parameter is not utilized in Laravel's Bcrypt implementation. The $value must be the plain text value to check, and the $hashedValue should be the previously generated hash. The check method internally makes a call to PHP's password_verify function.

The following code example will generate a hash for the string Ave Maria and perform some check if arbitrary strings are valid against the hashed value.

1<?php
2 
3/**
4 * Get a new instance of Hahser from the service container.
5 *
6 * @var \Illuminate\Contracts\Hashing\Hasher $hasher
7 */
8$hasher = app('Illuminate\Contracts\Hashing\Hasher');
9 
10// Generate a hash for "Ave Maria"
11$hash = $hasher->make('Ave Maria');
12 
13// true
14$isValid = $hasher->check('Ave Maria', $hash);
15 
16// false
17$isValid = $hasher->check('Ave maria', $hash);
18 
19// false
20$isValid = $hasher->check('password', $hash);

The following example will generate twenty hashes for the string Ave Maria and check the original string against all hashes. This demonstrates that even though the hashes are different each time a string is hashed, enough information is stored with the hash that a password can be checked successfully;

1<?php
2 
3/**
4 * Get a new instance of Hahser from the service container.
5 *
6 * @var \Illuminate\Contracts\Hashing\Hasher $hasher
7 */
8$hasher = app('Illuminate\Contracts\Hashing\Hasher');
9 
10// Generate 20 hashes for "Ave Maria"
11for ($i = 0; $i < 20; $i++) {
12 $validHashes[] = $hasher->make('Ave Maria');
13}
14 
15// Check "Ave Maria" against all 20 differnet hashes.
16// They should all be valid.
17foreach ($validHashes as $hash) {
18 $isValid = $hasher->check('Ave Maria', $hash);
19 
20 // Improve the readability of the output.
21 $isValid = ($isValid) ? 'true' : 'false';
22 
23 echo $hash, ' valid: ', $isValid, PHP_EOL;
24}

The above code would generate something similar to the following output (the exact hashes will change each time it is ran):

1$2y$10$0wgNNEz6N6avHmomWEPQI.GVZS32c8Kk.E1jAdUp0n4SuILQNohQC valid: true
2$2y$10$g81.C5MDdFfYXLMM6iRPJOW8frm6N6jAHYBzQT9TddbYFX2pDgt2W valid: true
3$2y$10$lWIUivhbnr8W3ILhquCqVuVl2E2esGNFSd4pueuVp/I7BbCNPI48. valid: true
4$2y$10$WIHxrlsgCpIqUZKIzi5AEe4cMWBCvFliwBas7YGMuJS4yDxI4f/Je valid: true
5$2y$10$XBq/FNfv87T0SXGBUd0UvO/azpuECPDYM7cLSKhszWBwu3I2m5aO2 valid: true
6$2y$10$El8v3pu2Ko4IQ82g4Xacd.NpJw3Pj4VLLoIJMIVmPDQqur9tSzG/i valid: true
7$2y$10$pW85CptXh9cC6XQ6HqKwTeBOrBWBMsogFRpNTHxvHc4U8bBUS/Wte valid: true
8$2y$10$5j6hK9uYpc3Vbp4ATsJh5O8MxTIzB8JMmgxLlULA6HjOd4mFnW13u valid: true
9$2y$10$W3rNwIfEfBMaiub1kxhW5u2b3lrwpLNzPajOc2CYy224s4WUEdpGW valid: true
10$2y$10$h6gEqC8IfQRmBflGD93BD.hNwNBYxQaMPqkKLqAySAfQziL2deYvS valid: true
11$2y$10$8QQ7RpXOfCIl0dZi0XcL1uuAFui6xCqbWLyIZxRWmwwYizK4ckOO2 valid: true
12$2y$10$RHhyoygqBS3Cb.kwXEihW.jLh.wOkIO6wjSZIqcK3HeStR.Jy/IFC valid: true
13$2y$10$jHd4HrR.2f.Vf7XIsVdPVuoIxUNMWpErwRZtiyffenG7adJd7AOJS valid: true
14$2y$10$CwK0aN9BtVYfb2yIIfbpduPJ1CFtvA028jK1EDZmH1PdpGwLO.b06 valid: true
15$2y$10$lsfaDaZqrt.n.UByvC90e.JXCKUa48uiUekue5zKDIJEFKEblkNQK valid: true
16$2y$10$.QoDYocdqXltvr4Q4E67V.tFpsXtuq3IshwCpMlanFES/sLYIUx4q valid: true
17$2y$10$MlfY7z9aKSudnkd00r6h5O0ZSt6B7qBel8jXEYGEaL7.zugM.lfbq valid: true
18$2y$10$4jvJYkkpTjlj1nwW8epIb.QLYK0nrDEnKmj/BfcGyTlIjaQO/2Dh6 valid: true
19$2y$10$XFEfJeDM.rTc3Kj4cMEYtegiRejRynLbn7aubaPzd0G3iHbf5VgVS valid: true
20$2y$10$.NDd6lPPSaJ0uRttNhB8K.kmk/i.1ntNI4/Apj.RtXkyDHncwAdNm valid: true

#needsRehash($hashedValue, array $options = [])

The needsRehash method is used to determine if the provided $hashedValue has been hashed with the supplied $options. This is useful for when the work factor of a hashing function has been updated and all passwords need to be updated to use the new hashing options. The following code example will demonstrate the usage of the needsRehash method. It also shows that two hashes generated with different $options will still be valid, but only one of them does not need to be rehashed.

1<?php
2 
3/**
4* Get a new instance of Hahser from the service container.
5*
6* @var \Illuminate\Contracts\Hashing\Hasher $hasher
7*/
8$hasher = app('Illuminate\Contracts\Hashing\Hasher');
9 
10// Generate a hash with fewer rounds than the default.
11$lessRounds = $hasher->make('Ave Maria', ['rounds' => 4]);
12 
13// Generate a hash with the default number of rounds.
14$defaultRounds = $hasher->make('Ave Maria');
15 
16// Generate a hash with more rounds than the default.
17$moreRounds = $hasher->make('Ave Maria', ['rounds' => 11]);
18 
19// All of these are valid.
20$isValid = $hasher->check('Ave Maria', $lessRounds);
21$isValid = $hasher->check('Ave Maria', $defaultRounds);
22$isValid = $hasher->check('Ave Maria', $moreRounds);
23 
24// This hash needs to be rehashed because it was generated with
25// fewer rounds than the current number of rounds (default).
26$needsRehash = $hasher->needsRehash($lessRounds);
27 
28// This hash does not need to be rehashed because it was generate
29// with the same number of rounds as the current number of rounds.
30$needsRehash = $hasher->needsRehash($defaultRounds);
31 
32// The $moreRounds hash also needs to be rehashed because it has more rounds
33// than the current number of rounds (the default number), even though it
34// has a higher work factor.
35$needsRehash = $hasher->needsRehash($moreRounds);

#The Hash Facade

Laravel provides many facades to make it easier to quickly build application and test ideas. One such facade is the Illuminate\Support\Facades\Hash facade, which provides access to whatever implementation is bound to the Illuminate\Contracts\Hashing\Hasher interface within the service container. Since the Hash facade resolves an implementation of Hasher, all Hasher methods can be used on the facade:

1<?php
2 
3use Illuminate\Support\Facades\Hash;
4 
5// All of the following calls are functionally
6// equivalent. The output will differ only
7// because bcrypt hashes will differ.
8$hash = Hash::make('test');
9$hash = app('hash')->make('test');
10$hash = app('Illuminate\Contracts\Hashing\Hasher')->make('test');

Some absolutely amazing
people

The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.