PHP
Tutorial PHP: Gestión de contraseñas y cifrado
PHP: Aprende a usar password_hash y password_verify para proteger contraseñas en aplicaciones web, garantizando seguridad frente a ataques.
Aprende PHP GRATIS y certifícateHashing de contraseñas con password_hash
y password_verify
En el desarrollo de aplicaciones web, es esencial proteger las contraseñas de los usuarios mediante técnicas de hashing seguras. PHP ofrece las funciones nativas password_hash
y password_verify
, que simplifican la implementación de criptografía robusta sin necesidad de manejar detalles complejos.
La función password_hash
permite encriptar una contraseña utilizando algoritmos fuertes y aplica automáticamente un salt aleatorio. Su uso básico es sencillo y garantiza que incluso contraseñas idénticas generen hashes diferentes, dificultando los ataques precomputados.
<?php
$contraseña = 'MiContraseñaSegura123';
$hash = password_hash($contraseña, PASSWORD_DEFAULT);
echo "Hash generado: $hash\n";
En el ejemplo anterior, password_hash
genera un hash utilizando el algoritmo predeterminado de PHP, que en las versiones más recientes es BCRYPT
. Este algoritmo incluye información sobre el salt y el costo utilizado, encapsulando todos los detalles necesarios para la verificación posterior.
Para verificar si una contraseña ingresada coincide con el hash almacenado, se utiliza password_verify
. Esta función compara la contraseña en texto plano con el hash y devuelve un valor booleano que indica si coinciden.
<?php
$contraseñaIngresada = 'MiContraseñaSegura123';
if (password_verify($contraseñaIngresada, $hash)) {
echo "¡Contraseña válida!\n";
} else {
echo "Contraseña incorrecta.\n";
}
Los hashes generados por password_hash
son únicos gracias al salt aleatorio incorporado. Esto significa que incluso si dos usuarios tienen la misma contraseña, los hashes almacenados serán distintos, fortaleciendo la seguridad frente a ataques de fuerza bruta o de diccionario.
Es posible ajustar el costo computacional del algoritmo para aumentar la dificultad de generar y crackear los hashes. Un costo más alto incrementa el tiempo necesario para procesar el hash, lo que puede mejorar la resistencia a ataques, aunque también afecta el rendimiento.
<?php
$options = [
'cost' => 12,
];
$hash = password_hash($contraseña, PASSWORD_BCRYPT, $options);
echo "Hash con costo personalizado: $hash\n";
PHP también soporta algoritmos más recientes como ARGON2I
y ARGON2ID
, considerados más seguros y eficientes. Para utilizarlos, se especifica el algoritmo deseado en password_hash
.
<?php
$hash = password_hash($contraseña, PASSWORD_ARGON2ID);
echo "Hash con Argon2id: $hash\n";
Para garantizar la compatibilidad y seguridad a futuro, se recomienda utilizar PASSWORD_DEFAULT
en password_hash
. De este modo, PHP seleccionará automáticamente el algoritmo más seguro disponible en versiones posteriores.
Es importante verificar si un hash necesita ser recalculado debido a actualizaciones en el algoritmo o cambios en las opciones de hashing. La función password_needs_rehash
permite determinar si es necesario actualizar el hash almacenado.
<?php
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
$nuevoHash = password_hash($contraseñaIngresada, PASSWORD_DEFAULT);
// Actualizar el hash en la base de datos
}
Al integrar estas funciones en la aplicación, es esencial manejar adecuadamente los posibles errores y excepciones que puedan surgir durante el proceso de hashing y verificación. Esto asegura un manejo robusto de contraseñas y contribuye a la seguridad integral del sistema.
Sal y algoritmos de encriptación
La utilización de una sal en el proceso de hashing es fundamental para reforzar la seguridad de las contraseñas almacenadas. Una sal es un valor aleatorio que se añade a la contraseña antes de aplicar el algoritmo de hashing, lo que garantiza que incluso contraseñas idénticas produzcan hashes distintos. Esto previene ataques como las tablas rainbow, que utilizan bases de datos de hashes precomputados para descifrar contraseñas.
En PHP, la función password_hash
gestiona automáticamente la generación y aplicación de la sal. No es necesario que el desarrollador genere o almacene manualmente la sal, ya que el algoritmo la incorpora de forma interna y segura. La sal se incluye en el propio hash resultante, permitiendo que password_verify
realice la verificación sin información adicional.
La elección del algoritmo de encriptación es otro aspecto crítico en la protección de contraseñas. PHP soporta varios algoritmos diseñados específicamente para el hashing seguro, cada uno con sus propias características y niveles de seguridad.
PASSWORD_BCRYPT
El algoritmo PASSWORD_BCRYPT
implementa el método Blowfish de encriptación adaptado para el hashing de contraseñas. Ofrece un equilibrio entre seguridad y rendimiento, y permite ajustar el coste computacional mediante el parámetro cost
. Un valor de cost
más alto incrementa el tiempo necesario para generar el hash, lo que dificulta los ataques de fuerza bruta.
<?php
$contraseña = 'MiContraseñaSegura123';
$options = ['cost' => 12];
$hash = password_hash($contraseña, PASSWORD_BCRYPT, $options);
echo "Hash con Bcrypt: $hash\n";
En este ejemplo, hemos especificado un cost
de 12, que es un valor comúnmente utilizado para aumentar la dificultad de los intentos de cracking sin afectar significativamente el rendimiento del servidor.
PASSWORD_ARGON2I y PASSWORD_ARGON2ID
Para mejorar la seguridad, especialmente en aplicaciones que manejan datos sensibles, se recomienda utilizar los algoritmos Argon2, introducidos en PHP 7.2 y disponibles en versiones posteriores. Argon2 fue el ganador del Password Hashing Competition y está diseñado para ser resistente a ataques de hardware avanzados como GPUs o ASICs.
<?php
$contraseña = 'MiContraseñaUltraSegura!@#';
$hash = password_hash($contraseña, PASSWORD_ARGON2ID);
echo "Hash con Argon2id: $hash\n";
El algoritmo PASSWORD_ARGON2ID
combina las fortalezas de PASSWORD_ARGON2I
y PASSWORD_ARGON2D
, proporcionando una mayor protección contra diferentes tipos de ataques. Argon2 permite personalizar varios parámetros para ajustar el consumo de recursos y la seguridad.
Personalización de Argon2
Con Argon2, es posible configurar la cantidad de memoria utilizada, el número de iteraciones y el grado de paralelismo. Estos parámetros permiten adaptar el algoritmo a las necesidades específicas de seguridad y rendimiento de la aplicación.
<?php
$options = [
'memory_cost' => 1 << 17, // 131072 KB de memoria
'time_cost' => 4, // 4 iteraciones
'threads' => 2, // 2 hilos
];
$hash = password_hash($contraseña, PASSWORD_ARGON2ID, $options);
echo "Hash con Argon2id personalizado: $hash\n";
En este código, hemos ajustado los parámetros para aumentar la complejidad del hashing. Un mayor memory_cost
y time_cost
dificultan los ataques al incrementar los recursos necesarios para calcular el hash.
Importancia de la entropía y el coste
Es crucial comprender que los algoritmos de hashing para contraseñas están diseñados para ser lentos y consumir recursos. Esto dificulta los ataques de fuerza bruta al requerir más tiempo y potencia computacional para probar combinaciones de contraseñas. Sin embargo, se debe encontrar un equilibrio para no afectar negativamente el rendimiento del sistema.
La entropía de la sal y el hash es esencial para garantizar la seguridad. PHP utiliza generadores aleatorios criptográficamente seguros, lo que significa que las sales y los hashes generados son impredecibles y resistentes a ataques estadísticos.
Hashing vs Cifrado
Es importante no confundir el hashing con el cifrado. El hashing es un proceso unidireccional que transforma la contraseña en un valor irreconocible, sin posibilidad de revertirlo para obtener la contraseña original. Esto es ideal para almacenar contraseñas, ya que solo es necesario verificar si una contraseña ingresada corresponde al hash almacenado.
El cifrado, por otro lado, es un proceso reversible que transforma datos legibles en datos cifrados, pudiendo ser descifrados posteriormente con la clave adecuada. En PHP, se pueden utilizar funciones como openssl_encrypt
y openssl_decrypt
para manejar cifrado simétrico, pero no se recomiendan para el almacenamiento de contraseñas.
<?php
$datos = 'Información confidencial';
$clave = 'claveSecreta12345';
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('AES-256-CBC'));
$cifrado = openssl_encrypt($datos, 'AES-256-CBC', $clave, 0, $iv);
echo "Datos cifrados: $cifrado\n";
$descifrado = openssl_decrypt($cifrado, 'AES-256-CBC', $clave, 0, $iv);
echo "Datos descifrados: $descifrado\n";
En este ejemplo, hemos cifrado y descifrado datos sensibles utilizando OpenSSL. Sin embargo, para el almacenamiento de contraseñas, siempre se debe optar por métodos de hashing seguros y no por cifrado.
Mejores prácticas
Para mantener la seguridad en la gestión de contraseñas y criptografía:
- Utilizar siempre funciones nativas y actualizadas de PHP para el hashing.
- No implementar métodos de hashing propios o utilizar algoritmos obsoletos.
- Evitar la reutilización de contraseñas y promover el uso de contraseñas fuertes entre los usuarios.
- Mantener PHP y todas las bibliotecas relacionadas actualizadas para aprovechar las últimas mejoras y correcciones de seguridad.
- Configurar adecuadamente los parámetros de los algoritmos para equilibrar seguridad y rendimiento.
La comprensión profunda de cómo funcionan la sal y los algoritmos de encriptación es esencial para cualquier desarrollador que desee proteger eficazmente las contraseñas y datos sensibles en sus aplicaciones.
Almacenamiento seguro de contraseñas
El almacenamiento seguro de contraseñas es un aspecto crítico en el desarrollo de aplicaciones web. No basta con hashear las contraseñas; es igualmente importante asegurar que los hashes se almacenen de forma segura en la base de datos y que no sean susceptibles a brechas de seguridad.
Evitar el almacenamiento en texto plano
Bajo ninguna circunstancia se deben almacenar contraseñas en texto plano. Hacerlo expone directamente las credenciales de los usuarios en caso de una intrusión. En cambio, es esencial almacenar únicamente los hashes generados mediante funciones seguras como password_hash
.
<?php
$contraseña = $_POST['contraseña'];
$hash = password_hash($contraseña, PASSWORD_DEFAULT);
// Código para almacenar $hash en la base de datos
Uso de consultas preparadas
Al interactuar con la base de datos, es fundamental utilizar consultas preparadas para prevenir inyecciones SQL. Las funciones de PDO o MySQLi con parámetros enlazados aseguran que los datos se escapen correctamente.
<?php
$usuario = $_POST['usuario'];
$hash = password_hash($_POST['contraseña'], PASSWORD_DEFAULT);
$dsn = 'mysql:host=localhost;dbname=mi_base_de_datos';
$pdo = new PDO($dsn, 'mi_usuario', 'mi_contraseña');
$stmt = $pdo->prepare('INSERT INTO usuarios (usuario, contraseña) VALUES (:usuario, :contraseña)');
$stmt->bindParam(':usuario', $usuario);
$stmt->bindParam(':contraseña', $hash);
$stmt->execute();
En este ejemplo, se utiliza PDO con consultas parametrizadas, lo cual protege contra ataques de inyección SQL, garantizando un almacenamiento más seguro de las contraseñas.
Restricción de permisos en la base de datos
Es recomendable limitar los permisos de la cuenta de base de datos que utiliza la aplicación. Otorgar únicamente los privilegios necesarios reduce el riesgo en caso de que un atacante obtenga acceso a las credenciales de la base de datos.
Por ejemplo, si la aplicación solo necesita realizar operaciones de lectura y escritura en determinadas tablas, no es necesario que tenga permisos de administrador.
Protección de las credenciales de la base de datos
Las credenciales de acceso a la base de datos deben protegerse adecuadamente. Evita incluirlas en el código fuente compartido o en repositorios públicos. Es preferible utilizar archivos de configuración externos, ubicados fuera del directorio raíz público, y establecer permisos correctos en el servidor.
<?php
// Archivo de configuración (config.php), ubicado fuera del directorio público
return [
'dsn' => 'mysql:host=localhost;dbname=mi_base_de_datos',
'usuario' => 'mi_usuario',
'contraseña' => 'mi_contraseña_segura',
];
Registro seguro de errores
Al manejar errores relacionados con las contraseñas o la autenticación, es crucial no revelar información sensible en los mensajes de error. Detalles como el formato del hash, los nombres de las columnas o mensajes de excepción pueden dar pistas a un atacante.
<?php
try {
// Código de conexión y ejecución
} catch (PDOException $e) {
// Registro interno del error
error_log($e->getMessage());
// Mensaje genérico al usuario
echo "Ocurrió un error al procesar su solicitud.\n";
}
En este caso, se registra el error para fines de auditoría, pero se proporciona un mensaje genérico al usuario, evitando la divulgación de información técnica.
Seguridad en las copias de seguridad
Las copias de seguridad de la base de datos deben manejarse con el mismo nivel de seguridad que la base de datos en producción. Es vital almacenar los respaldos en ubicaciones seguras y, preferiblemente, en formato cifrado.
# Ejemplo de encriptar una copia de seguridad con OpenSSL
mysqldump -u mi_usuario -p mi_base_de_datos | openssl enc -aes-256-cbc -salt -out respaldo.sql.enc
En este comando, la copia de seguridad se cifra utilizando AES de 256 bits, añadiendo una capa adicional de protección en caso de acceso no autorizado.
Implementación de políticas de contraseñas robustas
Fomentar el uso de contraseñas fuertes mejora significativamente la seguridad general. Implementar políticas que requieran una longitud mínima, combinación de caracteres y evitar contraseñas comunes, contribuye a fortalecer el sistema.
<?php
$contraseña = $_POST['contraseña'];
if (strlen($contraseña) < 8 || !preg_match('/[A-Z]/', $contraseña) || !preg_match('/[0-9]/', $contraseña)) {
echo "La contraseña debe tener al menos 8 caracteres, una mayúscula y un número.\n";
exit;
}
Este fragmento verifica que la contraseña cumpla con ciertos criterios de complejidad, mejorando la resistencia contra ataques de fuerza bruta.
Actualización y migración de hashes
Con el tiempo, es posible que desees actualizar el algoritmo de hashing utilizado. La función password_needs_rehash
ayuda a identificar si un hash existente necesita ser rehashado con un algoritmo más reciente o con opciones actualizadas.
<?php
$contraseñaIngresada = $_POST['contraseña'];
$hashAlmacenado = obtenerHashDeLaBaseDeDatos($usuario);
if (password_verify($contraseñaIngresada, $hashAlmacenado)) {
if (password_needs_rehash($hashAlmacenado, PASSWORD_DEFAULT)) {
$nuevoHash = password_hash($contraseñaIngresada, PASSWORD_DEFAULT);
actualizarHashEnLaBaseDeDatos($usuario, $nuevoHash);
}
echo "¡Inicio de sesión exitoso!\n";
} else {
echo "Contraseña incorrecta.\n";
}
Esta práctica permite mantener los hashes actualizados sin requerir que los usuarios cambien sus contraseñas, incrementando la seguridad pasivamente.
Manejo seguro de restablecimiento de contraseñas
El proceso de restablecimiento de contraseñas debe diseñarse cuidadosamente para evitar vulnerabilidades. Es recomendable utilizar tokens de un solo uso, generados aleatoriamente y con tiempo de expiración limitado.
<?php
$token = bin2hex(random_bytes(16));
// Almacenar $token asociado al usuario en la base de datos con una marca de tiempo
// Enviar enlace de restablecimiento al correo electrónico del usuario
$enlace = "https://www.ejemplo.com/restablecer.php?token=$token";
mail($correoUsuario, "Restablecimiento de contraseña", "Utiliza el siguiente enlace para restablecer tu contraseña: $enlace\n");
Al generar tokens seguros y evitar incluir información sensible en los enlaces, se reduce el riesgo de explotación. Además, es importante validar el token y verificar su expiración al procesar el restablecimiento.
Implementación de límites y bloqueos
Para protegerse contra intentos de fuerza bruta, conviene implementar límites en los intentos de inicio de sesión y bloquear temporalmente las cuentas tras múltiples intentos fallidos.
<?php
$intentosFallidos = obtenerIntentosFallidos($usuario);
if ($intentosFallidos >= 5) {
echo "Tu cuenta ha sido bloqueada temporalmente. Inténtalo más tarde.\n";
exit;
}
// Proceso de verificación de contraseña
if (password_verify($contraseñaIngresada, $hashAlmacenado)) {
restablecerIntentosFallidos($usuario);
echo "¡Inicio de sesión exitoso!\n";
} else {
incrementarIntentosFallidos($usuario);
echo "Contraseña incorrecta.\n";
}
Este enfoque limita la capacidad de un atacante para probar múltiples contraseñas en un corto periodo, añadiendo una capa adicional de seguridad.
Monitoreo y auditoría continua
Finalmente, es esencial mantener un monitoreo constante de las actividades relacionadas con la autenticación. Registrar eventos como inicios de sesión exitosos y fallidos, cambios de contraseña y restablecimientos permite detectar posibles intentos de intrusión.
<?php
function registrarEvento($usuario, $evento)
{
$registro = date('Y-m-d H:i:s') . " - Usuario: $usuario - Evento: $evento\n";
file_put_contents('auditoria.log', $registro, FILE_APPEND);
}
// Ejemplo de uso
registrarEvento($usuario, 'Inicio de sesión fallido');
Mediante la auditoría sistemática y el análisis de registros, es posible identificar patrones sospechosos y responder de manera proactiva a amenazas potenciales.
Todas las lecciones de PHP
Accede a todas las lecciones de PHP y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Php
Introducción Y Entorno
Instalación Y Primer Programa De Php
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Estructuras De Control
Sintaxis
Funciones Y Llamada De Funciones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Manejo De Números
Sintaxis
Manejo De Fechas Y Tiempo
Sintaxis
Manejo De Arrays
Sintaxis
Introducción A La Poo En Php
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Constructores Y Destructores
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Interfaces
Programación Orientada A Objetos
Traits
Programación Orientada A Objetos
Namespaces
Programación Orientada A Objetos
Autoloading De Clases
Programación Orientada A Objetos
Manejo De Errores Y Excepciones
Programación Orientada A Objetos
Manejo De Archivos
Programación Orientada A Objetos
Patrones De Diseño
Programación Orientada A Objetos
Introducción A Los Formularios En Php
Formularios
Procesamiento De Datos De Formularios
Formularios
Manejo De Archivos En Formularios
Formularios
Redirecciones Y Retroalimentación Al Usuario
Formularios
Formularios Dinámicos Y Separación De Lógica
Formularios
Introducción A La Persistencia En Php
Persistencia
Conexión A Bases De Datos
Persistencia
Consultas Y Operaciones Crud
Persistencia
Gestión De Transacciones
Persistencia
Manejo De Errores Y Excepciones En Base De Datos
Persistencia
Patrones De Acceso A Datos
Persistencia
Concepto De Sesiones En Php
Sesiones Y Cookies
Configuración De Sesiones
Sesiones Y Cookies
Cookies
Sesiones Y Cookies
Manejo Avanzado De Sesiones Y Cookies
Sesiones Y Cookies
Principales Vulnerabilidades En Php
Seguridad
Seguridad En Formularios Y Entrada De Datos
Seguridad
Protección Frente A Inyección Sql
Seguridad
Gestión De Contraseñas Y Cifrado
Seguridad
Seguridad En Sesiones Y Cookies
Seguridad
Configuraciones De Php Para Seguridad
Seguridad
Introducción Al Testing En Php
Testing
Phpunit
Testing
Cobertura De Código En Testing
Testing
Test Doubles (Mocks, Stubs, Fakes, Spies)
Testing
Pruebas De Integración Y Funcionales
Testing
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender el uso de
password_hash
para la encriptación segura de contraseñas. - Aprender a verificar contraseñas mediante
password_verify
. - Conocer la importancia del uso de salt en el hashing de contraseñas.
- Explorar la configuración de costos y parámetros en algoritmos de hashing.
- Familiarizarse con la elección de algoritmos como
BCRYPT
,ARGON2I
, yARGON2ID
. - Saber cuándo es necesario recalcular hashes con
password_needs_rehash
.