Session Fixation in Feathersjs
How Session Fixation Manifests in Feathersjs
Session fixation in Feathersjs applications typically occurs through improper session management during authentication flows. The vulnerability arises when an attacker can force a user to use a known session ID, allowing the attacker to hijack the session after the user authenticates.
In Feathersjs, this often manifests in authentication hooks and service configurations. Consider a common pattern where authentication is handled manually without proper session invalidation:
const { authenticate } = require('@feathersjs/authentication');
app.service('users').hooks({
before: {
create: [authenticate('jwt')],
login: [
authenticate('local'),
async (context) => {
// Vulnerable: session ID not regenerated
const { user } = context.result;
context.result.user = await context.app.service('users').get(user.id);
}
]
}
});The vulnerability appears when a fixed session ID is accepted without regeneration. An attacker could create a session, obtain the session ID, and trick a victim into authenticating with that session. Feathersjs applications using express-session or similar middleware are particularly vulnerable if they don't regenerate session IDs upon authentication.
Another manifestation occurs in Feathersjs when using the authentication service without proper session handling. The default configuration may not invalidate existing sessions when a user logs in from a new device or location:
app.configure(authentication({
jwt: {
header: { typ: 'access' },
audience: 'https://yourdomain.com',
issuer: 'feathers',
algorithm: 'HS256',
expiresIn: '1d'
}
}));Without session invalidation logic, an attacker who obtains a session ID through various means (network sniffing, XSS, or social engineering) can maintain access even after the legitimate user changes their password. This is especially problematic in Feathersjs applications that maintain long-lived sessions for "remember me" functionality.
Session fixation also appears in Feathersjs when using custom authentication strategies that don't properly handle session state. For example:
class CustomStrategy extends Strategy {
async authenticate(authentication, params) {
const { email, password } = authentication.payload;
const user = await this.app.service('users').find({
email, password
});
// Vulnerable: existing session not invalidated
if (user) {
return this.success(user);
}
return this.error(new Error('Invalid credentials'));
}
}This pattern allows an attacker to fixate a session before authentication completes, maintaining access to the authenticated session.
Feathersjs-Specific Detection
Detecting session fixation vulnerabilities in Feathersjs requires examining both code patterns and runtime behavior. The middleBrick API security scanner specifically tests for session fixation by attempting to authenticate with pre-existing session IDs and monitoring whether the server accepts them without regeneration.
Code analysis for session fixation in Feathersjs should focus on authentication hooks and service configurations. Look for patterns where session IDs are accepted without validation or regeneration. The middleBrick CLI tool can scan your Feathersjs application for these patterns:
npx middlebrick scan http://localhost:3030
# Or scan specific authentication endpoints
npx middlebrick scan http://localhost:3030/authenticationThe scanner tests for session fixation by creating sessions, attempting authentication with those sessions, and verifying whether the server properly invalidates and regenerates session IDs. It checks common Feathersjs authentication patterns including JWT, local, and custom strategies.
Runtime detection involves monitoring session behavior during authentication. Use middleware to log session ID changes:
const sessionLogger = async (context) => {
const oldSessionId = context.params.session?.id;
const result = await context.app.service('authentication').create(context.data, context.params);
const newSessionId = context.params.session?.id;
if (oldSessionId && oldSessionId === newSessionId) {
console.warn('Potential session fixation: session ID not regenerated during authentication');
}
return result;
};middleBrick's scanning engine specifically tests Feathersjs applications by simulating the following attack pattern: create a session, authenticate with that session, then verify whether the session ID changes. If the session ID remains the same, the application is vulnerable to session fixation.
The scanner also checks for proper session timeout handling and concurrent session management, which are related to session fixation vulnerabilities. It verifies that Feathersjs applications properly invalidate sessions when users log out or when sessions expire.
Feathersjs-Specific Remediation
Remediating session fixation in Feathersjs requires implementing proper session management throughout the authentication lifecycle. The most critical fix is regenerating session IDs upon successful authentication. Feathersjs provides several approaches to achieve this.
First, use the built-in authentication service with proper configuration:
app.configure(authentication({
session: true,
jwt: {
header: { typ: 'access' },
audience: 'https://yourdomain.com',
issuer: 'feathers',
algorithm: 'HS256',
expiresIn: '1d'
}
}));Then implement a hook to regenerate session IDs after authentication:
const { authenticate } = require('@feathersjs/authentication');
app.service('authentication').hooks({
before: {
create: [
authenticate('jwt'),
async (context) => {
// Regenerate session ID after successful authentication
if (context.params.session) {
await context.params.session.regenerate();
}
}
]
}
});For custom authentication strategies, ensure session regeneration is part of the success flow:
class CustomStrategy extends Strategy {
async authenticate(authentication, params) {
const { email, password } = authentication.payload;
const user = await this.app.service('users').find({
email, password
});
if (user) {
// Regenerate session to prevent fixation
if (params.session) {
await params.session.regenerate();
}
return this.success(user);
}
return this.error(new Error('Invalid credentials'));
}
}Implement proper session timeout and invalidation policies. Use Feathersjs's session management capabilities to automatically expire sessions:
app.configure(expressSession({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true,
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000 // 24 hours
},
rolling: true,
unset: 'destroy'
}));Add logout hooks to ensure sessions are properly destroyed:
app.service('authentication').hooks({
after: {
remove: [
async (context) => {
// Destroy session on logout
if (context.params.session) {
await context.params.session.destroy();
}
}
]
}
});For applications using JWT tokens with Feathersjs, implement token rotation to prevent fixation attacks:
const jwt = require('jsonwebtoken');
const rotateToken = async (context) => {
const oldToken = context.params.jwt;
if (oldToken) {
// Verify and issue new token
const payload = jwt.verify(oldToken, process.env.JWT_SECRET);
const newToken = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1d'
});
context.result.accessToken = newToken;
}
return context;
};Finally, implement IP and user agent validation to detect session hijacking attempts. While not directly preventing fixation, this adds an additional security layer:
const validateSession = async (context) => {
const { session } = context.params;
if (session && session.user) {
const { ip, userAgent } = context.params;
if (session.ip !== ip || session.userAgent !== userAgent) {
await session.destroy();
throw new Error('Session validation failed');
}
}
return context;
};