Password Hashing

To manage the security of our users, we never store their passwords in plaintext format, we store the password´s hash instead. Hashing is not a reversible operation, the password cannot be recovered. The password can be cracked though and to make cracking as hard as possible we have to use a secure algorithm. Class Nette\Security\Passwords will help us with that.

Installation and requirements

The framework automatically adds a Nette\Security\Passwords service to the DI container under the name security.passwords, which you get by passing it using dependency injection:

use Nette\Security\Passwords;

class Foo
{
	public function __construct(
		private Passwords $passwords,
	) {
	}
}

__construct($algo=PASSWORD_DEFAULT, array $options=[])string

Chooses which secure algorithm is used for hashing and how to configure it.

The default is PASSWORD_DEFAULT, so the algorithm choice is left to PHP. Algorithm may change in newer PHP releases when newer, stronger hashing algorithms are supported. Therefore you should be aware that the length of the resulting hash can change. Therefore you should store the resulting hash in a way that can store enough characters, 255 is the recommended width.

This is how you'd use the bcrypt algorithm and change the hashing speed using the cost parameter from the default 10. In year 2020, with cost 10, the hashing of one password takes roughly 80ms, cost 11 takes 160ms, cost 12 then 320ms, the scale is logarithmic. The slower the better, cost 10–12 is considered slow enough for most use cases:

// we will hash passwords with 2^12 (2^cost) iterations of the bcrypt algorithm
$passwords = new Passwords(PASSWORD_BCRYPT, ['cost' => 12]);

With dependency injection:

services:
	security.passwords: Nette\Security\Passwords(::PASSWORD_BCRYPT, [cost: 12])

hash(string $passwords): string

Generates password´s hash.

$res = $passwords->hash($password); // Hashes the password

The result $res is a string that, in addition to the hash itself, contains the identifier of the algorithm used, its settings, and cryptographic salt (random data to ensure that a different hash is generated for the same password). It is therefore backwards compatible, for example, if you change the parameters, the hashes stored using the previous settings can be verified. This entire result is stored in the database, so there is no need to store salt or settings separately.

verify(string $password, string $hash)bool

Finds out, whether the given password matches the given hash. Get the $hash from database by username or email address.

if ($passwords->verify($password, $hash)) {
	// Correct password
}

needsRehash(string $hash): bool

Finds out if the hash matches the options given in constructor.

Use this method when you're for example changing hashing parameters. Password verification will use parameters stored with the hash and if needsRehash() returns true, you have to calculate the hash again, this time with the updated parameters, and again store it in database. This ensures that passwords hashes will be automatically “upgraded” when users are signing in.

if ($passwords->needsRehash($hash)) {
	$hash = $passwords->hash($password);
	// store $hash to database
}
version: 4.0 3.x 2.x