El protocolo HTTP es sin estado, lo que implica que cada solicitud es independiente y no retiene datos de interacciones previas. Para mantener el contexto del usuario, se utilizan diversas estrategias como cookies, sesiones y tokens.
Cookies en HTTP
Las cookies son un componente del protocolo HTTP que permite a los servidores enviar datos a los clientes para su almacenamiento y posterior envío en solicitudes. El proceso se inicia cuando el servidor incluye un encabezado Set-Cookie en la respuesta, especificando datos en formato nombre=valor. El navegador almacena esta cookie y la incluye automáticamente en futuras solicitudes al mismo servidor.
Parámetros adicionales que afectan el comportamiento de las cookies incluyen:
- path: Define la ruta URL a la que se aplica la cookie.
- expires y maxAge: Establecen la duración de la cookie; sin estos, se crea una cookie de sesión que se elimina al cerrar el navegador.
- secure: Indica que la cookie solo debe evniarse sobre HTTPS.
- httpOnly: Restringe el acceso a la cookie desde JavaScript, mitigando ataques XSS.
En aplicaciones basadas en Node.js con Express, las cookies se gestionan mediante módulos como cookie-parser. Un ejemplo básico podría configurarse así:
const express = require('express');
const cookieParser = require('cookie-parser');
const aplicacion = express();
aplicacion.use(cookieParser());
aplicacion.get('/', (req, res) => {
if (req.cookies.token_sesion) {
res.send('Sesión activa detectada');
} else {
res.cookie('token_sesion', 'identificador_unico', { maxAge: 180000 }); // 3 minutos
res.send('Cookie establecida por primera vez');
}
});
aplicacion.listen(8080);
Las cookies de sesión sin tiempo de expiración se eliminan al cerrar el navegador, mientras que aquellas con maxAge persisten según el intervalo definido.
Sesiones del Lado del Servidor
Las cookies almacenan datos en el cliente, lo que puede representar riesgos de seguridad para información sensible. Las sesiones resuelven esto manteniendo los datos en el servidor, asociados a un identificador único (session_id) que se envía al cliente mediante una cookie.
Al recibir una solicitud, el servidor utiliza el session_id para recuperar los datos de sesión almacenados. Este ID suele ser una cadena aleatoria compleja, dificultando su falsificación.
Los almacenes de sesiones comunes incluyen memoria, bases de datos o sistemas de caché como Redis. En entornos de producción, se prefiere el caché por su eficiencia.
Con Express y el módulo express-session, la configuración puede ser:
const express = require('express'); const session = require('express-session'); const aplicacion = express();
aplicacion.use(session({ secret: 'clave_secreta_compleja', name: 'id_sesion_personalizado', cookie: { secure: false, maxAge: 3600000 }, // 1 hora resave: false, saveUninitialized: true }));
aplicacion.get('/perfil', (req, res) => {
if (req.session.visitas) {
req.session.visitas++;
res.send(Visitas: ${req.session.visitas});
} else {
req.session.visitas = 1;
res.send('Primera visita');
}
});
aplicacion.listen(3000);
<p>La opción secret se utiliza para firmar el ID de sesión, previniendo alteraciones. Las cookies firmadas (signedCookies) añaden una capa de seguridad al encluir un hash basado en un secreto compartido.</p>
<h2>Tokens y Autenticación Stateless</h2>
<p>En arquitecturas basadas en APIs, donde el cliente y el servidor están desacoplados, las sesiones tradicionales pueden no ser viables. Los tokens permiten la autenticación sin estado, ideal para APIs RESTful y aplicaciones móviles.</p>
<p>Los tokens como JWT (JSON Web Tokens) encapsulan información de autenticación en un objeto compacto y seguro. Un JWT se compone de tres partes codificadas en Base64: encabezado, carga útil y firma.</p>
<p>El encabezado especifica el algoritmo de firma, por ejemplo:</p>
<code>{
"alg": "HS256",
"typ": "JWT"
}
</code>
<p>La carga útil contiene claims como issuer (iss) y expiration (exp). Tras firmar, el token resultante es una cadena URL-safe.</p>
<p>En una aplicación Node.js con Express, la implementación puede usar bibliotecas como jsonwebtoken:</p>
<code>const express = require('express');
const jwt = require('jsonwebtoken');
const aplicacion = express();
const CLAVE_SECRETA = 'mi_clave_secreta_para_jwt';
aplicacion.post('/login', (req, res) => {
const usuario = req.body.usuario;
const contrasena = req.body.contrasena;
// Validación de credenciales (lógica simplificada)
if (usuario && contrasena) {
const payload = { sub: usuario, role: 'usuario' };
const token = jwt.sign(payload, CLAVE_SECRETA, { expiresIn: '1h' });
res.json({ token });
} else {
res.status(401).send('Credenciales inválidas');
}
});
aplicacion.get('/recurso', (req, res) => {
const authHeader = req.headers.authorization;
if (authHeader) {
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, CLAVE_SECRETA);
res.send(`Acceso concedido para ${decoded.sub}`);
} catch (error) {
res.status(403).send('Token inválido');
}
} else {
res.status(401).send('Token no proporcionado');
}
});
aplicacion.listen(5000);
</code>
<p>Los tokens eliminan la necesidad de almacenar sesiones en el servidor, escalando mejor en sistemas distribuidos. Sin embargo, requieren manejo cuidadoso de expiración y revocación.</p>