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
}