Broken Authentication in Express
How Broken Authentication Manifests in Express
Broken authentication in Express applications often stems from improper session management and flawed credential handling. Express's default session middleware uses insecure cookie configurations by default, creating immediate vulnerabilities. When developers use express-session without setting secure: true, httpOnly: true, or sameSite: 'strict', session cookies become susceptible to interception and cross-site request forgery attacks.
A common Express-specific pattern that leads to broken authentication involves improper middleware ordering. If authentication middleware is placed after route handlers that should be protected, attackers can bypass authentication entirely. For example:
app.get('/admin', (req, res) => { // Vulnerable: no auth check
res.json(adminData);
});
app.use(authenticate); // Too late!
This ordering allows unauthenticated access to sensitive endpoints. Express developers also frequently mishandle passport.js strategies, leaving authentication incomplete. A typical mistake is failing to check req.isAuthenticated() in protected routes:
app.get('/profile', (req, res) => {
if (!req.user) { // Missing this check is catastrophic
return res.status(401).json({error: 'Unauthorized'});
}
res.json(req.user);
});
Session fixation attacks are particularly effective against Express apps that don't regenerate session IDs after login. Attackers can pre-generate valid session cookies and force victims to use them, maintaining persistent access even after password changes.
Express-Specific Detection
Detecting broken authentication in Express requires examining both configuration and runtime behavior. Start by inspecting your package.json for outdated authentication dependencies like express-session or passport. Version 1.17.1 of express-session had a critical vulnerability allowing session fixation through predictable session IDs.
middleBrick's Express-specific scanning identifies broken authentication through several automated checks. The scanner tests for default session configurations, attempting to establish sessions without credentials. It examines cookie attributes, looking for missing secure flags or httpOnly settings that expose sessions to client-side JavaScript.
The scanner also performs credential stuffing detection by testing common weak passwords against authentication endpoints. For Express applications using JWTs, middleBrick checks for weak signing secrets and improper token validation. The scanner can identify endpoints that accept expired tokens or fail to validate token structure properly.
npm install -g middlebrick
middlebrick scan https://yourapi.com --output json
This command returns a detailed report showing authentication vulnerabilities specific to your Express setup. The report includes severity ratings and references to OWASP API Top 10 categories like 'Broken Authentication' and 'Insufficient Logging & Monitoring'.
Express-Specific Remediation
Fixing broken authentication in Express requires implementing defense-in-depth strategies. Start with proper session configuration:
const session = require('express-session');
const MongoStore = require('connect-mongo');
app.use(session({
secret: process.env.SESSION_SECRET || crypto.randomBytes(32).toString('hex'),
store: MongoStore.create({ mongoUrl: process.env.MONGO_URI }),
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
sameSite: 'strict',
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
}));
Always regenerate session IDs upon authentication state changes to prevent fixation attacks:
app.post('/login', async (req, res) => {
const user = await User.authenticate(req.body.email, req.body.password);
if (!user) {
return res.status(401).json({error: 'Invalid credentials'});
}
req.session.regenerate((err) => {
if (err) return res.status(500).json({error: 'Session error'});
req.session.userId = user.id;
res.json({message: 'Login successful'});
});
});
Implement proper middleware ordering and authentication guards:
const authenticate = (req, res, next) => {
if (!req.session.userId) {
return res.status(401).json({error: 'Authentication required'});
}
next();
};
// Protect all routes under /api/private
app.use('/api/private', authenticate);
// Or protect individual routes
app.get('/api/profile', authenticate, (req, res) => {
res.json({user: req.session.userId});
});
For JWT-based authentication, use express-jwt with proper validation:
const jwt = require('express-jwt');
app.use(jwt({
secret: process.env.JWT_SECRET,
algorithms: ['HS256'],
issuer: 'your-app',
audience: 'your-audience'
}).unless({
path: ['/login', '/public']
}));
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |