Seguridad en PHP: Sesiones, Contraseñas y CSRF
Tres piezas imprescindibles antes de poner un login en producción
🗂️ 1. Sesiones con $_SESSION
Estado actual de la sesión:
Guarda algo en tu sesión
💡 ¿Por qué funciona esto entre recargas?
PHP guarda tus datos de sesión en el servidor y solo envía al navegador una cookie con un ID ( PHPSESSID). Por eso session_start() debe llamarse antes de imprimir cualquier HTML: necesita poder enviar esa cookie en las cabeceras.
🔒 2. Hash de contraseñas: password_hash() / password_verify()
Hash "guardado" en el servidor (contraseña real: password123):
Prueba a "iniciar sesión"
💡 Nunca guardes contraseñas en texto plano
password_hash() genera un hash distinto cada vez (incluye una "sal" aleatoria), aunque la contraseña sea la misma. Por eso nunca comparas contraseñas con ==: usas password_verify(), que sabe extraer esa sal del propio hash.
🛡️ 3. Protección CSRF con tokens
Token válido actual en $_SESSION:
Envía el formulario protegido
💡 ¿Qué evita un token CSRF?
Que otra web engañe al navegador del usuario para enviar un formulario a la tuya sin que él lo sepa. Como ese token secreto vive en la sesión del servidor y en un campo oculto del formulario legítimo, un atacante externo no puede adivinarlo.
🎯 Conceptos Clave
Sesiones
session_start()antes de cualquier salida- Datos guardados en el servidor, no en el navegador
Hashing
password_hash()para guardarpassword_verify()para comprobar
CSRF
- Token secreto por sesión
- Comparar con
hash_equals(), nunca con==
💪 Ejercicio Práctico
Refuerza el sistema
- Añade un contador de intentos fallidos de contraseña en
$_SESSIONy bloquea tras 3 intentos. - Regenera el
session_id()consession_regenerate_id(true)justo después de un login correcto. - Añade una cookie de sesión con
httponlyysamesite=Strictensession_set_cookie_params().
Solución 1
$_SESSION['intentos'] = ($_SESSION['intentos'] ?? 0) + 1;
if ($_SESSION['intentos'] >= 3) {
die(t('Demasiados intentos, inténtalo más tarde.'));
}
Solución 2
if (password_verify($password, $hashGuardado)) {
session_regenerate_id(true);
$_SESSION['usuario_id'] = $usuario['id'];
}
Solución 3
session_set_cookie_params([
'httponly' => true,
'samesite' => 'Strict',
'secure' => true
]);
session_start();