Sessions del lado del servidor en Express.js | Nicolás Garzón
Una session mantiene estado autenticado en el servidor y entrega al cliente un identificador opaco. Su principal ventaja es la revocación centralizada; su coste es operar un store compartido.
El navegador conserva una cookie con un ID aleatorio:
Texto
Copiar cookie session-id
↓
session store
↓
userId, expiración y metadataLa identidad real no necesita viajar en cada request. El servidor puede invalidar la session en cualquier momento.
Después del login, el servidor necesita reconocer requests posteriores. Guardar toda la identidad en una cookie expone y replica estado. Una session permite almacenar solo un identificador difícil de adivinar.
El cliente envía credenciales por HTTPS.
El servidor verifica password y estado de cuenta.
Se regenera el ID para evitar fixation.
Se guarda una session en un store.
Se envía una cookie HttpOnly/Secure.
En requests posteriores, middleware carga la session.
Logout destruye el registro y borra la cookie.
TypeScript
Copiar app. use ( session ( {
name: '__Host-session' ,
secret: sessionSecrets,
resave: false ,
saveUninitialized: false ,
cookie: {
httpOnly: true ,
secure: true ,
sameSite: 'lax' ,
path: '/' ,
maxAge: 1000 * 60 * 60 ,
} ,
store,
} ) ) ; La cookie contiene el ID firmado, no todo el objeto session.
El store por defecto no sirve para producción:
pierde sessions al reiniciar
vive dentro de un proceso
no comparte datos entre instancias
puede crecer sin control
Utiliza un store mantenido, como Redis o una base adecuada, con expiración y observabilidad.
Un atacante puede intentar hacer que la víctima use un ID conocido. Después de autenticar, regenera:
TypeScript
Copiar request. session. regenerate ( ( error) => {
if ( error) return next ( error) ;
request. session. userId = user. id;
request. session. save ( ( saveError) => {
if ( saveError) return next ( saveError) ;
response. sendStatus ( 204 ) ;
} ) ;
} ) ; La regeneración cambia el ID y evita conservar una session pre-login.
userId
timestamps
nivel de autenticación/MFA
metadata necesaria para revocación
No guardes perfiles enormes, entidades ORM ni permisos que deban reflejar cambios inmediatos sin estrategia de invalidación.
Expira tras inactividad. Puede renovarse con rolling sessions.
Limita la vida total incluso con actividad.
Combinar ambos reduce sessions eternas. Una session sensible puede requerir reautenticación reciente para acciones críticas.
session individual
todas las sessions del usuario
sessions anteriores a cambio de password
sessions con dispositivo sospechoso
Mantén índices o versionado que permitan hacerlo eficientemente.
TypeScript
Copiar request. session. destroy ( ( error) => {
if ( error) return next ( error) ;
response. clearCookie ( '__Host-session' , cookieOptions) ;
response. sendStatus ( 204 ) ;
} ) ; Borrar la cookie sin destruir el store deja una session activa si el ID se recupera.
Con store compartido, múltiples instancias pueden resolver la misma session sin sticky sessions. El store se convierte en dependencia crítica:
latencia por request
disponibilidad
capacidad
expiración
serialización
Cachear session localmente puede mejorar latencia pero complica revocación.
Dos requests pueden modificar la misma session simultáneamente y sobrescribirse. Evita utilizar la session como base de estado de negocio mutable. Almacena identidad y contexto pequeño; el dominio vive en su base.
ID aleatorio con alta entropía.
Cookie Secure, HttpOnly y SameSite.
Regeneración al elevar privilegios.
Rate limiting de login.
Invalidación al cambiar password.
No registrar ID completo.
Protección CSRF para mutations con cookie.
TLS en todo el trayecto relevante.
TypeScript
Copiar const authenticateFromSession: RequestHandler = async ( request, response, next) => {
const userId = request. session. userId;
if ( ! userId) {
response. status ( 401 ) . json ( { code: 'AUTHENTICATION_REQUIRED' } ) ;
return ;
}
const actor = await users. findActiveActor ( userId) ;
if ( ! actor) {
request. session. destroy ( ( ) => undefined ) ;
response. status ( 401 ) . json ( { code: 'INVALID_SESSION' } ) ;
return ;
}
response. locals. actor = actor;
next ( ) ;
} ; La session identifica. El servidor vuelve a resolver estado actual cuando permisos o activación importan.
La API quizá no pueda autenticar. Responde indisponibilidad sin tratar al usuario como anónimo si eso cambia seguridad.
Un store externo conserva sessions. El MemoryStore las perdería.
Invalida sessions existentes o usa una sessionVersion comparada con usuario.
Revocación, expiración corta, device metadata y reautenticación reducen impacto.
Evita contadores o carrito crítico dentro del objeto session sin control de concurrencia.
MemoryStore en producción.
No regenerar tras login.
Guardar objetos grandes.
Confiar en permisos almacenados indefinidamente.
Borrar cookie sin destruir session.
Session sin expiración absoluta.
No proteger CSRF.
Marcar errores del store como “usuario no autenticado”.
login y regeneración
cookie attributes
session expirada
logout y revocación
cambio de password
múltiples instancias
store caído
concurrencia
CSRF
rotación de secretos
Sessions facilitan revocación y navegadores, pero añaden un lookup y store. Tokens autocontenidos reducen lookup, pero complican revocación y estado obsoleto. La elección depende de clientes, escala y threat model.
La cookie transporta un ID opaco; el estado vive en servidor.
Regenera al autenticar o elevar privilegios.
El store debe ser compartido, durable según necesidad y observable.
Session no debe almacenar todo el dominio.
Cookies autenticadas requieren analizar CSRF.
¿Por qué MemoryStore no escala?
¿Qué ataque reduce la regeneración tras login?
¿Por qué borrar solo la cookie no es logout completo?
¿Qué problema produce modificar la misma session desde requests concurrentes?
Ver respuestas
Vive en un proceso, se pierde al reiniciar y no comparte datos.
Session fixation.
El registro server-side sigue activo si el ID vuelve a usarse.
Actualizaciones perdidas o sobrescritura de estado.
Tokens y JWT con criterio compara estado server-side con credenciales firmadas y sus límites de revocación.