PHP

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ícate

Hashing 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.

Para seguir leyendo hazte Plus

¿Ya eres Plus? Accede a la app

20 % DE DESCUENTO

Plan mensual

19.00 /mes

15.20 € /mes

Precio normal mensual: 19 €
58 % DE DESCUENTO

Plan anual

10.00 /mes

8.00 € /mes

Ahorras 132 € al año
Precio normal anual: 120 €
Aprende PHP GRATIS online

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

PHP

Introducción Y Entorno

Instalación Y Primer Programa De Php

PHP

Introducción Y Entorno

Tipos De Datos, Variables Y Constantes

PHP

Sintaxis

Operadores Y Expresiones

PHP

Sintaxis

Estructuras De Control

PHP

Sintaxis

Funciones Y Llamada De Funciones

PHP

Sintaxis

Cadenas De Texto Y Manipulación

PHP

Sintaxis

Manejo De Números

PHP

Sintaxis

Manejo De Fechas Y Tiempo

PHP

Sintaxis

Manejo De Arrays

PHP

Sintaxis

Introducción A La Poo En Php

PHP

Programación Orientada A Objetos

Clases Y Objetos

PHP

Programación Orientada A Objetos

Constructores Y Destructores

PHP

Programación Orientada A Objetos

Herencia

PHP

Programación Orientada A Objetos

Encapsulación

PHP

Programación Orientada A Objetos

Polimorfismo

PHP

Programación Orientada A Objetos

Interfaces

PHP

Programación Orientada A Objetos

Traits

PHP

Programación Orientada A Objetos

Namespaces

PHP

Programación Orientada A Objetos

Autoloading De Clases

PHP

Programación Orientada A Objetos

Manejo De Errores Y Excepciones

PHP

Programación Orientada A Objetos

Manejo De Archivos

PHP

Programación Orientada A Objetos

Patrones De Diseño

PHP

Programación Orientada A Objetos

Introducción A Los Formularios En Php

PHP

Formularios

Procesamiento De Datos De Formularios

PHP

Formularios

Manejo De Archivos En Formularios

PHP

Formularios

Redirecciones Y Retroalimentación Al Usuario

PHP

Formularios

Formularios Dinámicos Y Separación De Lógica

PHP

Formularios

Introducción A La Persistencia En Php

PHP

Persistencia

Conexión A Bases De Datos

PHP

Persistencia

Consultas Y Operaciones Crud

PHP

Persistencia

Gestión De Transacciones

PHP

Persistencia

Manejo De Errores Y Excepciones En Base De Datos

PHP

Persistencia

Patrones De Acceso A Datos

PHP

Persistencia

Concepto De Sesiones En Php

PHP

Sesiones Y Cookies

Configuración De Sesiones

PHP

Sesiones Y Cookies

Cookies

PHP

Sesiones Y Cookies

Manejo Avanzado De Sesiones Y Cookies

PHP

Sesiones Y Cookies

Principales Vulnerabilidades En Php

PHP

Seguridad

Seguridad En Formularios Y Entrada De Datos

PHP

Seguridad

Protección Frente A Inyección Sql

PHP

Seguridad

Gestión De Contraseñas Y Cifrado

PHP

Seguridad

Seguridad En Sesiones Y Cookies

PHP

Seguridad

Configuraciones De Php Para Seguridad

PHP

Seguridad

Introducción Al Testing En Php

PHP

Testing

Phpunit

PHP

Testing

Cobertura De Código En Testing

PHP

Testing

Test Doubles (Mocks, Stubs, Fakes, Spies)

PHP

Testing

Pruebas De Integración Y Funcionales

PHP

Testing

Accede GRATIS a PHP y certifícate

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, y ARGON2ID.
  • Saber cuándo es necesario recalcular hashes con password_needs_rehash.