Curso de PHP
Sesiones y cookies en PHP: cómo recordar al usuario
Por Víctor Peña · Publicado el
Hola, ¿cómo están? Segundo tema complementario del curso de PHP: cómo hacer que tu aplicación recuerde quién es el usuario mientras navega.
¡Empecemos!
El problema: HTTP no recuerda nada
Cada petición al servidor es independiente de las anteriores. El navegador pide una página, PHP la genera, y todo lo que había en memoria se descarta.
Eso significa que si un usuario inicia sesión en una página, en la siguiente el servidor ya no sabe quién es. Se dice que HTTP es un protocolo sin estado.
Las sesiones y las cookies son las dos formas de resolverlo.
Cookies: datos en el navegador
Una cookie es un pequeño dato que el servidor le pide al navegador que guarde, y que el navegador reenvía en cada petición siguiente.
<?php
// Guardar
setcookie('idioma', 'es', [
'expires' => time() + 86400 * 30, // 30 días
'path' => '/',
'secure' => true, // solo por HTTPS
'httponly' => true, // JavaScript no puede leerla
'samesite' => 'Lax', // protege contra CSRF
]);
// Leer
$idioma = $_COOKIE['idioma'] ?? 'es';
// Borrar
setcookie('idioma', '', ['expires' => time() - 3600, 'path' => '/']);
Dos detalles importantes:
setcookie() debe llamarse antes de cualquier salida. Si ya imprimiste HTML, obtendrás el error «headers already sent». Es el mismo motivo por el que recomendamos no cerrar con ?> los archivos que solo contienen PHP.
La cookie no está disponible en la misma petición. La guardas ahora y la lees a partir de la siguiente carga.
Las opciones de seguridad no son opcionales
httponlyimpide que JavaScript la lea. Es lo que limita el daño de un ataque XSS.securehace que solo viaje por HTTPS.samesiteevita que se envíe desde otros sitios, protegiendo contra CSRF.
Y la regla de fondo: nunca guardes información sensible en una cookie. El usuario puede verla y modificarla. Nada de precios, permisos ni identificadores de usuario sin firmar.
Sesiones: datos en el servidor
Una sesión guarda los datos en el servidor y solo envía al navegador un identificador. Es lo que quieres para todo lo importante.
<?php
session_start(); // siempre antes de cualquier salida
$_SESSION['usuario_id'] = 42;
$_SESSION['nombre'] = 'Víctor';
// En cualquier otra página:
session_start();
echo $_SESSION['nombre'] ?? 'Invitado';
session_start() hace dos cosas: crea la sesión si no existe, o recupera la existente a partir de la cookie de identificación.
Tiene que ir en la primera línea, antes de imprimir nada.
Comprobar y eliminar datos
<?php
session_start();
if (isset($_SESSION['usuario_id'])) {
echo "Sesión activa";
}
unset($_SESSION['carrito']); // borra un dato
$_SESSION = []; // vacía todos
Cerrar sesión correctamente
Esto se hace mal muy a menudo. No basta con vaciar el array:
<?php
session_start();
// 1. Vaciar los datos
$_SESSION = [];
// 2. Borrar la cookie de sesión
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
}
// 3. Destruir la sesión en el servidor
session_destroy();
header('Location: login.php');
exit;
Los tres pasos son necesarios. Si solo vacías $_SESSION, el identificador sigue vivo y podría reutilizarse.
Regenerar el identificador al iniciar sesión
Esta línea evita un ataque real llamado fijación de sesión, y casi nadie la pone:
<?php
session_start();
// Tras verificar usuario y contraseña:
session_regenerate_id(true);
$_SESSION['usuario_id'] = $usuario['id'];
El ataque consiste en que alguien fuerce a la víctima a usar un identificador de sesión conocido; cuando la víctima inicia sesión, el atacante ya tiene una sesión válida. Regenerar el identificador en ese momento lo anula.
Hazlo siempre después de un login exitoso.
Configuración segura de sesiones
Al inicio de tu aplicación, antes del session_start():
<?php
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_secure', '1'); // requiere HTTPS
ini_set('session.use_strict_mode', '1');
ini_set('session.cookie_samesite', 'Lax');
session_start();
use_strict_mode hace que PHP rechace identificadores de sesión que él no generó, que es otra protección contra la fijación.
Un login básico
Juntemos esto con lo que vimos de bases de datos y formularios:
<?php
declare(strict_types=1);
session_start();
$error = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$correo = trim($_POST['correo'] ?? '');
$clave = $_POST['clave'] ?? '';
$sentencia = $pdo->prepare("SELECT id, nombre, clave_hash FROM usuarios WHERE correo = :correo");
$sentencia->execute(['correo' => $correo]);
$usuario = $sentencia->fetch();
if ($usuario && password_verify($clave, $usuario['clave_hash'])) {
session_regenerate_id(true);
$_SESSION['usuario_id'] = $usuario['id'];
$_SESSION['nombre'] = $usuario['nombre'];
header('Location: panel.php');
exit;
}
$error = 'Correo o contraseña incorrectos';
}
Dos cosas críticas aquí:
Las contraseñas nunca se guardan en texto plano. Se guarda un hash generado con password_hash():
<?php
$hash = password_hash($clave, PASSWORD_DEFAULT);
Y se comprueba con password_verify(). Nunca compares contraseñas con === ni uses md5() o sha1(), que están rotos para este uso.
El mensaje de error es genérico. Si dijeras «el correo no existe», estarías confirmando qué correos están registrados en tu sistema.
Proteger páginas privadas
<?php
// auth.php
session_start();
if (!isset($_SESSION['usuario_id'])) {
header('Location: login.php');
exit;
}
Y en cada página protegida:
<?php
require 'auth.php';
Sesión o cookie: cuál usar
| Sesión | Cookie | |
|---|---|---|
| Dónde se guarda | Servidor | Navegador |
| Puede modificarla el usuario | No | Sí |
| Sobrevive al cerrar el navegador | No, por defecto | Sí |
| Para datos sensibles | Sí | Nunca |
La regla: sesión para todo lo que importe —quién es el usuario, sus permisos, el carrito—, cookie solo para preferencias sin valor, como el idioma o el modo oscuro.
Errores comunes
- Llamar a
session_start()después de imprimir algo. - Guardar datos sensibles en cookies.
- Cerrar sesión solo vaciando
$_SESSION. - No regenerar el identificador tras el login.
- Guardar contraseñas en texto plano o con
md5(). - Confiar en una cookie para decidir permisos.
Para cerrar
Las sesiones son la forma de que tu aplicación recuerde al usuario de forma segura; las cookies, para preferencias sin importancia. Y dos líneas que no puedes olvidar: session_regenerate_id(true) tras el login, y password_hash() para las contraseñas.
En el siguiente artículo reunimos todas las prácticas de seguridad del curso.
Saludos y éxitos.
Norvic Software
Desarrollamos el software que tu empresa necesita
Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.
- Chatbots con Inteligencia Artificial
- Creación de agentes de IA
- Integración de IA en tus sistemas
- Desarrollo de software a medida
- Aplicaciones móviles iOS y Android
- Consultoría y asesoramiento técnico
Cotización sin costo · Respuesta directa por WhatsApp