Session Fixation in Express
How Session Fixation Manifests in Express
Session fixation in Express applications occurs when an attacker can set or predict a victim's session identifier before authentication, allowing them to hijack the session after the victim logs in. This vulnerability is particularly dangerous in Express because of how session middleware handles session IDs by default.
// Vulnerable Express session setup
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: false } // HTTP only, no secure flag
}));The vulnerability manifests in several Express-specific ways:
- Predictable session IDs: By default, express-session uses a simple incrementing counter or weak random number generator, making session IDs predictable.
- Uninitialized session creation: With
saveUninitialized: true, sessions are created even for unauthenticated users, giving attackers a target. resave: falsecombined withsaveUninitialized: truemeans sessions persist across requests without authentication.
Common Express-specific attack patterns include:
// Attacker crafts a URL with their session ID
app.get('/login', (req, res) => {
// Attacker sends victim: http://example.com/login?sessionid=abc123
if (req.query.sessionid) {
req.sessionID = req.query.sessionid; // INSECURE: allows session fixation
}
res.render('login');
});Another vulnerability appears in Express applications that expose session IDs through URLs or headers:
// Vulnerable: session ID in URL
app.get('/api/data', (req, res) => {
const sessionId = req.query.sid; // Attacker-controlled session ID
// Uses attacker's session instead of creating new one
req.sessionID = sessionId;
res.json({ data: getSessionData(sessionId) });
});Session fixation also occurs when Express applications fail to regenerate sessions after privilege escalation:
// Vulnerable: no session regeneration after login
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = authenticate(username, password);
if (user) {
req.session.user = user; // Still using old session ID!
res.redirect('/dashboard');
}
});Express-Specific Detection
Detecting session fixation in Express requires examining both code patterns and runtime behavior. middleBrick's black-box scanning identifies these vulnerabilities without requiring source code access:
Code Pattern Analysis
// middleBrick CLI scan
middlebrick scan https://api.example.com/login --category session-managementThe scanner tests for:
- Session ID predictability through statistical analysis of generated IDs
- Exposure of session IDs in URLs, headers, or response bodies
- Missing session regeneration after authentication
- Unsafe session configuration parameters
Runtime Detection
middleBrick actively tests Express applications by:
# Testing session fixation vulnerability
curl -c cookies.txt "https://api.example.com/login"
# Get initial session ID
SESSION_ID=$(grep -o 'connect\.sid=[^;]*' cookies.txt | cut -d'=' -f2)
# Attempt fixation
curl -b "connect\.sid=$SESSION_ID" "https://api.example.com/login" -c cookies2.txt
# Check if session persisted
FIXATED_ID=$(grep -o 'connect\.sid=[^;]*' cookies2.txt | cut -d'=' -f2)
if [ "$SESSION_ID" = "$FIXATED_ID" ]; then
echo "Session fixation vulnerability detected"
fiConfiguration Analysis
middleBrick analyzes Express session middleware configuration for risky defaults:
// middleBrick reports these as high-risk configurations
{
"session_config": {
"saveUninitialized": true, // High risk
"resave": false, // Medium risk
"cookie": {
"secure": false, // High risk
"httpOnly": false // High risk
}
}
}Express-Specific Remediation
Fixing session fixation in Express requires both secure configuration and proper session lifecycle management. Here's the secure Express session setup:
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: process.env.SESSION_SECRET, // Use strong, random secret
resave: false,
saveUninitialized: false, // Only create session when needed
cookie: {
secure: true, // Require HTTPS
httpOnly: true, // Prevent JS access
sameSite: 'strict', // Prevent CSRF
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
}));Session Regeneration After Authentication
app.post('/login', (req, res, next) => {
const { username, password } = req.body;
authenticate(username, password)
.then(user => {
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// CRITICAL: Regenerate session to prevent fixation
req.session.regenerate(err => {
if (err) return next(err);
req.session.user = user;
req.session.authenticated = true;
res.json({ message: 'Login successful' });
});
})
.catch(next);
});Session Invalidation on Logout
app.post('/logout', (req, res) => {
// Destroy session completely
req.session.destroy(err => {
if (err) return res.status(500).json({ error: 'Logout failed' });
// Clear session cookie
res.clearCookie('connect.sid');
res.json({ message: 'Logged out successfully' });
});
});Middleware for Session Security
// Custom middleware to prevent session fixation
function sessionSecurity(req, res, next) {
// Check for session ID in URL parameters
if (req.query.sessionid || req.query.sid) {
return res.status(400).json({
error: 'Session ID cannot be provided in URL'
});
}
// Check for session fixation attempts
if (req.session && req.session.attemptedFixation) {
return res.status(403).json({
error: 'Suspicious session activity detected'
});
}
next();
}
app.use(sessionSecurity);Testing Your Fixes
After implementing these fixes, verify with middleBrick's continuous monitoring:
# Pro plan: continuous monitoring
middlebrick monitor https://api.example.com --schedule daily --alert slackThe scanner will attempt session fixation attacks and verify that your session regeneration and configuration changes are effective.