Sesiones y cookies en PHP: login, logout y manejo de usuarios


Después de aprender a trabajar con formularios, PDO y un CRUD completo, el siguiente paso natural en PHP es entender cómo mantener información de un usuario entre distintas páginas.

Para eso PHP ofrece dos mecanismos fundamentales:

  • sesiones, mediante $_SESSION;
  • cookies, almacenadas en el navegador del usuario.

En esta guía vamos a utilizarlas para construir la base de un sistema de login y logout con PHP y MySQL.

¿Qué problema resuelven las sesiones?

HTTP es un protocolo sin estado. Eso significa que, por defecto, cada petición que hacemos a una página web es independiente de la anterior.

Si un usuario inicia sesión en login.php y después entra a panel.php, necesitamos alguna forma de recordar que ese usuario ya fue autenticado.

Las sesiones permiten guardar información del usuario en el servidor y asociarla a sus siguientes peticiones.

Iniciar una sesión con session_start()

Antes de utilizar $_SESSION debemos iniciar la sesión:

<?php

session_start();

?>

En general, session_start() debe ejecutarse antes de enviar contenido HTML al navegador.

Guardar datos en $_SESSION

Podemos guardar valores como si se tratara de un array asociativo:

<?php

session_start();

$_SESSION['usuario_id'] = 15;
$_SESSION['usuario_nombre'] = 'Ana';

?>

Estos valores estarán disponibles en otras páginas mientras la sesión continúe activa.

Leer datos de sesión

<?php

session_start();

if (isset($_SESSION['usuario_id'])) {
    echo 'Usuario autenticado: ' . $_SESSION['usuario_nombre'];
}

?>

Crear la tabla de usuarios

Para construir un login necesitamos almacenar los usuarios en la base de datos.

CREATE TABLE usuarios (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(150) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    activo BOOLEAN DEFAULT TRUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

La columna password tendrá suficiente espacio para almacenar el hash generado por PHP.

Nunca guardar contraseñas en texto plano

Una contraseña no debe almacenarse directamente en la base de datos.

PHP incluye password_hash(), que genera un hash seguro:

<?php

$password = 'MiClaveSegura123';

$hash = password_hash($password, PASSWORD_DEFAULT);

echo $hash;

?>

El resultado será diferente de la contraseña original y es lo que debemos guardar en la base.

Registrar un usuario con PDO

<?php

$nombre = $_POST['nombre'] ?? '';
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';

$hash = password_hash($password, PASSWORD_DEFAULT);

$sql = 'INSERT INTO usuarios (nombre, email, password)
        VALUES (:nombre, :email, :password)';

$stmt = $pdo->prepare($sql);

$stmt->execute([
    'nombre' => $nombre,
    'email' => $email,
    'password' => $hash
]);

?>

Formulario de login

<form method="POST" action="login.php">

    <label>Email</label>
    <input type="email" name="email" required>

    <label>Contraseña</label>
    <input type="password" name="password" required>

    <button type="submit">Ingresar</button>

</form>

Buscar al usuario por email

Cuando recibimos el formulario, buscamos al usuario mediante una consulta preparada:

<?php

session_start();

$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';

$sql = 'SELECT id, nombre, email, password, activo
        FROM usuarios
        WHERE email = :email
        LIMIT 1';

$stmt = $pdo->prepare($sql);
$stmt->execute([
    'email' => $email
]);

$usuario = $stmt->fetch(PDO::FETCH_ASSOC);

?>

Validar la contraseña con password_verify()

No debemos comparar la contraseña ingresada con el hash utilizando ==.

PHP proporciona password_verify():

if (
    $usuario
    && $usuario['activo']
    && password_verify($password, $usuario['password'])
) {
    // Login correcto
}

Crear la sesión después del login

Si los datos son correctos, guardamos solo la información necesaria en la sesión:

if (
    $usuario
    && $usuario['activo']
    && password_verify($password, $usuario['password'])
) {
    session_regenerate_id(true);

    $_SESSION['usuario_id'] = $usuario['id'];
    $_SESSION['usuario_nombre'] = $usuario['nombre'];

    header('Location: panel.php');
    exit;
}

session_regenerate_id(true) ayuda a reducir el riesgo de ataques de fijación de sesión después de autenticar al usuario.

Ejemplo completo de login.php

<?php

session_start();

require 'conexion.php';

$error = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    $email = trim($_POST['email'] ?? '');
    $password = $_POST['password'] ?? '';

    $sql = 'SELECT id, nombre, password, activo
            FROM usuarios
            WHERE email = :email
            LIMIT 1';

    $stmt = $pdo->prepare($sql);

    $stmt->execute([
        'email' => $email
    ]);

    $usuario = $stmt->fetch(PDO::FETCH_ASSOC);

    if (
        $usuario
        && $usuario['activo']
        && password_verify($password, $usuario['password'])
    ) {
        session_regenerate_id(true);

        $_SESSION['usuario_id'] = $usuario['id'];
        $_SESSION['usuario_nombre'] = $usuario['nombre'];

        header('Location: panel.php');
        exit;
    }

    $error = 'Email o contraseña incorrectos';
}

?>

Es mejor mostrar un mensaje genérico como “Email o contraseña incorrectos” en lugar de revelar si el email existe o no.

Proteger una página privada

En panel.php podemos verificar si existe la sesión:

<?php

session_start();

if (!isset($_SESSION['usuario_id'])) {
    header('Location: login.php');
    exit;
}

?>

Después podemos mostrar información del usuario:

<h1>
    Bienvenido,
    <?= htmlspecialchars($_SESSION['usuario_nombre']) ?>
</h1>

Crear logout.php

Para cerrar sesión:

<?php

session_start();

$_SESSION = [];

session_destroy();

header('Location: login.php');
exit;

?>

¿Qué es una cookie?

Una cookie es un pequeño valor almacenado por el navegador y enviado posteriormente al servidor.

Podemos crear una cookie con setcookie():

<?php

setcookie(
    'tema',
    'oscuro',
    [
        'expires' => time() + 3600 * 24 * 30,
        'path' => '/',
        'httponly' => true,
        'samesite' => 'Lax'
    ]
);

?>

Leer una cookie

<?php

$tema = $_COOKIE['tema'] ?? 'claro';

echo $tema;

?>

Sesiones vs cookies

Aunque están relacionadas, no cumplen exactamente la misma función.

Sesiones

  • los datos principales se mantienen en el servidor;
  • son apropiadas para identificar a un usuario autenticado;
  • normalmente el navegador conserva solo un identificador de sesión.

Cookies

  • se almacenan en el navegador;
  • pueden sobrevivir al cierre del navegador según su fecha de expiración;
  • son útiles para preferencias y otras necesidades persistentes;
  • el usuario puede modificarlas, por lo que nunca debemos confiar ciegamente en su contenido.

No guardar contraseñas en cookies

Nunca debemos hacer algo como:

setcookie('password', $password);

Las contraseñas no deben almacenarse en cookies, sesiones, archivos de texto ni registros de log en texto plano.

¿Y la opción “Recordarme”?

Una función de “Recordarme” no debería guardar directamente el identificador del usuario ni su contraseña como mecanismo de autenticación.

Una implementación seria utiliza un token aleatorio almacenado de forma segura en la base de datos y una cookie asociada a ese token.

Ese tema merece una implementación específica y es mejor abordarlo después de comprender bien sesiones, seguridad y autenticación.

Configurar cookies de sesión de forma más segura

En una aplicación servida mediante HTTPS podemos mejorar la configuración de la cookie de sesión:

<?php

session_set_cookie_params([
    'httponly' => true,
    'secure' => true,
    'samesite' => 'Lax'
]);

session_start();

?>

secure debe utilizarse cuando el sitio trabaja mediante HTTPS.

Evitar guardar información innecesaria en la sesión

No necesitamos guardar el registro completo del usuario.

Normalmente alcanza con almacenar identificadores y algunos datos mínimos:

$_SESSION['usuario_id'] = $usuario['id'];
$_SESSION['usuario_nombre'] = $usuario['nombre'];

Si necesitamos información actualizada, podemos consultar nuevamente la base utilizando el identificador.

Errores comunes

  • olvidar ejecutar session_start();
  • enviar HTML antes de utilizar header() o setcookie();
  • guardar contraseñas en texto plano;
  • comparar contraseñas sin password_verify();
  • guardar datos sensibles dentro de cookies;
  • no regenerar el identificador de sesión después del login;
  • no verificar la sesión en cada página privada;
  • mostrar mensajes que permiten descubrir si una cuenta existe;
  • confiar en valores provenientes de cookies como si fueran datos seguros.

Ejercicio práctico

Creá un pequeño sistema con estas páginas:

  1. registro.php: crear un usuario utilizando password_hash();
  2. login.php: validar email y contraseña;
  3. panel.php: permitir el acceso solo a usuarios autenticados;
  4. logout.php: finalizar la sesión;
  5. una cookie para recordar una preferencia como el tema claro u oscuro.

¿Qué sigue?

En la próxima guía podemos avanzar con autenticación y autorización en PHP: roles, permisos, protección de rutas y control de acceso para distintos tipos de usuarios.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador para seguir publicando ejercicios, proyectos y guías gratuitas, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Descubre más desde Club Programador

Suscríbete y recibe las últimas entradas en tu correo electrónico.

Deja un comentario

Descubre más desde Club Programador

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo