Session Fixation in Chi with Dynamodb
Session Fixation in Chi with Dynamodb — how this specific combination creates or exposes the vulnerability
Session fixation occurs when an application assigns a user a session identifier before authentication and allows that identifier to be used after login. In a Chi-based service that stores session state in DynamoDB, this typically means the session ID is generated early, written to the DynamoDB table, and then accepted back from the client without revalidation or regeneration after authentication.
Chi routes are often composed as a series of handlers. If a route sets a session cookie using a predictable or unvalidated identifier and then stores session metadata (such as user ID, permissions, or freshness) in a DynamoDB table, an attacker can force a known session ID on a victim. When the victim authenticates, the attacker can use the same session ID to hijack the authenticated session. This pattern is common when session data is stored server-side in DynamoDB and the session ID is exposed in URLs or not rotated on privilege change.
The DynamoDB schema itself does not cause the flaw, but the way the session record is created and looked up can amplify risk. For example, using a static partition key or an easily guessable sort key (e.g., timestamp-based IDs) makes it feasible for an attacker to enumerate or predict valid session identifiers. If the application writes the session to DynamoDB before confirming the user’s credentials, and then trusts the incoming session ID on subsequent requests without confirming ownership, the fixation condition is realized.
Real-world attack patterns mirror the OWASP API Top 10:2023 broken object level authorization (BOLA) and authentication weaknesses. An attacker can combine session fixation with BOLA by taking over a session and then manipulating resource identifiers to access other users’ data stored in DynamoDB. Because the session record in DynamoDB may contain authorization metadata, a fixed session can lead to privilege escalation if the application does not re-check identity and permissions on each request.
middleBrick scans can detect indicators of this risk by correlating session handling logic with DynamoDB access patterns. Even in a black-box scan, findings related to Authentication, BOLA/IDOR, and Unsafe Consumption can highlight places where session identifiers are accepted without proper revalidation after authentication. Remediation guidance from such scans often recommends regenerating session identifiers after login and ensuring DynamoDB session records are tightly scoped and validated.
Dynamodb-Specific Remediation in Chi — concrete code fixes
To remediate session fixation in Chi when using DynamoDB, regenerate the session identifier after successful authentication and ensure the DynamoDB record is created only after credentials are verified. Do not trust client-provided session IDs for authenticated operations.
Below is a concrete example using the AWS SDK for JavaScript v3 with Chi and a DynamoDB table designed for session storage. The code demonstrates secure session creation, lookup, and cleanup with a focus on avoiding fixation.
import { DynamoDBClient, PutItemCommand, GetItemCommand, DeleteItemCommand } from "@aws-sdk/client-dynamodb";
import { randomUUID } from "crypto";
import { contentType, bodyParser, sessionCookie } from "@quintype/chi-middleware";
const client = new DynamoDBClient({ region: "us-east-1" });
const TABLE_NAME = process.env.SESSION_TABLE;
async function ensureSessionTable() {
// Ensure table exists with a partition key `sessionId` (string)
// This is a one-time setup step, not part of request handling.
}
async function writeSession(sessionId, userId, expiresAt) {
const cmd = new PutItemCommand({
TableName: TABLE_NAME,
Item: {
sessionId: { S: sessionId },
userId: { S: userId },
expiresAt: { N: String(Math.floor(expiresAt.getTime() / 1000)) },
},
});
await client.send(cmd);
}
async function readSession(sessionId) {
const cmd = new GetItemCommand({
TableName: TABLE_NAME,
Key: {
sessionId: { S: sessionId },
},
});
const res = await client.send(cmd);
return res.Item ? res.Item : null;
}
async function deleteSession(sessionId) {
const cmd = new DeleteItemCommand({
TableName: TABLE_NAME,
Key: {
sessionId: { S: sessionId },
},
});
await client.send(cmd);
}
// Chi route composition
import { route } from "@quintype/chi";
export const authRoutes = route(
// Login handler: verify credentials, then regenerate session
async (req, res, next) => {
const { username, password } = await bodyParser.json(req);
// Validate username/password against your user store
const validUser = await validateUser(username, password);
if (!validUser) {
res.statusCode = 401;
return contentType.json(res, { error: "Invalid credentials" });
}
// Invalidate any existing session associated with this user if needed
// ...
// Create a new session identifier after authentication
const newSessionId = randomUUID();
const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24);
// Write the new session to DynamoDB only after successful auth
await writeSession(newSessionId, validUser.id, expiresAt);
// Set secure cookie with the new session ID
sessionCookie.set(res, newSessionId, {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 86400,
});
contentType.json(res, { ok: true });
},
// Protected handler: validate session from DynamoDB, do not trust cookie alone
async (req, res, next) => {
const sessionId = sessionCookie.get(req);
if (!sessionId) {
res.statusCode = 401;
return contentType.json(res, { error: "Unauthorized" });
}
const item = await readSession(sessionId);
if (!item) {
res.statusCode = 401;
return contentType.json(res, { error: "Invalid or expired session" });
}
// Attach user info to request for downstream handlers
req.user = {
id: item.userId.S,
};
return next();
}
);
Key points in this remediation:
- Session identifier is generated with
randomUUID()only after credentials are validated, preventing an attacker from pre-setting the session ID. - Session metadata is stored in DynamoDB keyed by the new session ID, and the cookie is set with
HttpOnly,Secure, andSameSite=Strictattributes. - On each authenticated request, the session ID from the cookie is looked up in DynamoDB; the application does not trust the cookie value for authorization decisions beyond existence and validity checks.
- Consider adding a last-access timestamp and conditional TTL in DynamoDB for additional security and cleanup; ensure partition key design avoids enumeration (e.g., use UUID rather than sequential IDs).
If you use the middleBrick CLI to scan this Chi + DynamoDB setup, it can surface findings related to Authentication and BOLA/IDOR, along with prioritized remediation guidance. For teams seeking deeper integration, the Pro plan’s continuous monitoring can keep session storage security in check across changes, while the GitHub Action can fail builds if risk scores exceed your defined threshold.