Hash de contraseñas
Para garantizar la seguridad de nuestros usuarios, nunca guardamos sus contraseñas de forma legible, sino solo su huella (llamada hash). El hash no se puede revertir para obtener la contraseña original. Es importante usar un algoritmo seguro para crear el hash. Con eso nos ayuda la clase Nette\Security\Passwords.
El framework añade automáticamente al contenedor DI un servicio del tipo Nette\Security\Passwords con el nombre
security.passwords. Puede obtenerlo haciendo que se lo pasen mediante dependency injection.
use Nette\Security\Passwords;
class Foo
{
public function __construct(
private Passwords $passwords,
) {
}
}
__construct (string $algo=PASSWORD_DEFAULT, array $options=[])
Elegimos qué algoritmo seguro usar para generar el hash y configuramos sus parámetros.
El valor predeterminado es PASSWORD_DEFAULT, es decir, la elección del algoritmo se deja a PHP. El algoritmo
puede cambiar en versiones más recientes de PHP si aparecen algoritmos de hash nuevos y más fuertes. Por eso debe tener en
cuenta que la longitud del hash resultante puede cambiar, y debería guardarlo de forma que quepan suficientes caracteres; se
recomienda un ancho de 255.
Ejemplo de cómo establecer la velocidad de hashing del algoritmo bcrypt cambiando el parámetro cost: (en 2020 el valor predeterminado es 10, hacer el hash de una contraseña lleva unos 80 ms; con cost 11 son aprox. 160 ms; con cost 12, aprox. 320 ms; cuanto más lento, mejor la protección, y una velocidad de 10–12 ya se considera protección suficiente)
// haremos el hash de las contraseñas con 2^12 (2^cost) iteraciones del algoritmo bcrypt
$passwords = new Passwords(PASSWORD_BCRYPT, ['cost' => 12]);
Mediante dependency injection:
services:
security.passwords: Nette\Security\Passwords(::PASSWORD_BCRYPT, [cost: 12])
static bcrypt (?int $cost=null): Passwords
Crea una instancia configurada para el algoritmo bcrypt. El parámetro $cost establece la velocidad de hashing
descrita arriba; si lo omite, se usa el valor predeterminado de PHP.
$passwords = Passwords::bcrypt(12);
static argon2id (?int $memoryCost=null, ?int $timeCost=null, ?int $threads=null): Passwords
Crea una instancia configurada para el algoritmo Argon2id. Los parámetros omitidos se dejan a los valores predeterminados de
PHP. Si PHP se compiló sin soporte para Argon2, el método lanza Nette\NotSupportedException.
$passwords = Passwords::argon2id(memoryCost: 1 << 17, timeCost: 4);
hash (string $password): string
Genera el hash de la contraseña.
$res = $passwords->hash($password); // Hace el hash de la contraseña
El resultado $res es una cadena que, además del hash en sí, contiene el identificador del algoritmo usado, su
configuración y una sal criptográfica (datos aleatorios que aseguran que para la misma contraseña se genere un hash distinto).
Por tanto es retrocompatible; por ejemplo, si cambia los parámetros, los hashes guardados con la configuración anterior se
siguen pudiendo verificar. Todo ese resultado se guarda en la base de datos, así que no hace falta guardar por separado la sal ni
la configuración.
verify (string $password, string $hash): bool
Averigua si la contraseña dada corresponde al hash dado. $hash lo obtiene de la base de datos según el nombre de
usuario o la dirección de correo introducidos.
if ($passwords->verify($password, $hash)) {
// contraseña correcta
}
needsRehash (string $hash): bool
Averigua si el hash corresponde a las opciones indicadas en el constructor.
Resulta útil usarlo cuando, por ejemplo, cambia el cost del hashing. La verificación se hace según la configuración
guardada, y si needsRehash() devuelve true, hay que crear el hash de nuevo, esta vez con los parámetros
nuevos, y volver a guardarlo en la base de datos. Así los hashes guardados se “actualizan” automáticamente cuando los
usuarios inician sesión.
if ($passwords->needsRehash($hash)) {
$hash = $passwords->hash($password);
// guarda $hash en la base de datos
}