Auth Bypass in Hapi with Dynamodb
Auth Bypass in Hapi with DynamoDB — how this specific combination creates or exposes the vulnerability
An auth bypass in a Hapi application that uses DynamoDB typically arises from incomplete authorization checks combined with how DynamoDB requests are constructed and evaluated. Hapi provides a flexible routing and authentication model, but if route pre‑handlers or policies rely only on the presence of a token or on coarse IAM permissions, an attacker may exploit misaligned authorization logic.
Consider a DynamoDB-based user profile endpoint that retrieves items using a user identifier from a JWT claim. If the route does not enforce that the requesting user can only access their own data, and the server trusts the client-supplied identifier, an authenticated user can modify the request to reference another user’s ID. Because DynamoDB permissions are often managed at a broad table level, the service identity used by the application might have dynamodb:GetItem on the table, but lacks a row-level constraint. This mismatch between authentication (token validity) and authorization (resource ownership) creates an authorization bypass.
Another common pattern is the use of DynamoDB ConditionExpressions for soft deletes or status checks. If the server omits status validation in the condition and only checks for item existence, an attacker who knows another user’s key might read data they should not see. The attack flow is straightforward: intercept or guess another user’s key, send a request to the Hapi route, and observe whether DynamoDB returns the item. If it does, the application conflates authentication with proper authorization, which maps to the OWASP API Top 10 —2023 API1:2023 Broken Object Level Authorization (BOLA), a frequent finding in scans.
OpenAPI/Swagger analysis can highlight inconsistencies between declared security schemes and runtime behavior. For example, if an operation is marked as requiring a scope that the route handler does not validate against DynamoDB queries, the scan can flag the endpoint as high risk. Real-world scans have identified endpoints where DynamoDB queries omitted the user identifier filter entirely, returning items across partitions due to a missing key condition. Such findings emphasize why the scan’s 12 checks, including Authentication, BOLA/IDOR, and Property Authorization, run in parallel to correlate spec definitions with runtime behavior.
DynamoDB-Specific Remediation in Hapi
Remediation centers on enforcing ownership checks in application logic and tightening DynamoDB request construction. In Hapi, use a pre‑handler or policy that extracts the authenticated subject from the request and ensures it matches the DynamoDB key used in the query. Never rely on client‑supplied identifiers without re‑deriving the key from the authenticated context.
Below is a concrete example using the AWS SDK for JavaScript v3 with Hapi. The handler extracts userId from the auth credentials, builds the key, and issues a GetItem request with a condition that also validates the item status. This ensures that even if an attacker guesses another user’s ID, the request fails if the status does not match or the user ID does not align with the token.
import { DynamoDBClient, GetItemCommand, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
import { unmarshall } from "@aws-sdk/util-dynamodb";
const client = new DynamoDBClient({ region: "us-east-1" });
export default async function profileHandler(request, h) {
// The user ID is derived from the authenticated session, not from request parameters.
const userId = request.auth.credentials.userId;
if (!userId) {
return h.response({ error: "Unauthorized" }).code(401);
}
const params = {
TableName: process.env.PROFILE_TABLE,
Key: {
pk: { S: `USER#${userId}` },
sk: { S: `PROFILE` }
},
// ConditionExpression ensures the item is active and owned by this user.
ConditionExpression: "attribute_exists(sk) AND status = :active",
ExpressionAttributeValues: {
":active": { S: "ACTIVE" }
}
};
try {
const command = new GetItemCommand(params);
const data = await client.send(command);
if (!data.Item) {
return h.response({ error: "Not found" }).code(404);
}
return unmarshall(data.Item);
} catch (err) {
if (err.name === "ConditionalCheckFailedException") {
return h.response({ error: "Forbidden" }).code(403);
}
return h.response({ error: "Internal server error" }).code(500);
}
}
For listing items, apply the same ownership principle in the query key and filter. Use DynamoDB’s partition key to scope the query to the authenticated user, and avoid scanning or using broad secondary indexes that expose other users’ data. The following example shows a query restricted to a user’s own items, with an additional filter on a server-controlled status attribute.
import { DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
import { unmarshall } from "@aws-sdk/util-dynamodb";
const client = new DynamoDBClient({ region: "us-east-1" });
export default async function listItemsHandler(request, h) {
const userId = request.auth.credentials.userId;
if (!userId) {
return h.response({ error: "Unauthorized" }).code(401);
}
const params = {
TableName: process.env.ITEMS_TABLE,
IndexName: "UserIdIndex",
KeyConditionExpression: "userId = :uid AND status = :active",
FilterExpression: "visibility = :public",
ExpressionAttributeValues: {
":uid": { S: `USER#${userId}` },
":active": { S: "ACTIVE" },
":public": { S: "true" }
}
};
try {
const command = new QueryCommand(params);
const data = await client.send(command);
return data.Items.map(unmarshall);
} catch (err) {
return h.response({ error: "Internal server error" }).code(500);
}
}
These patterns align with the scan’s checks for Authentication, BOLA/IDOR, and Property Authorization. By validating ownership on the server and using condition expressions to enforce status constraints, you reduce the attack surface that an auth bypass could exploit. The dashboard can track improvements over time, and the Pro plan’s continuous monitoring can alert you if a future scan detects a regression in authorization logic.
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 |