Bola Idor in Hapi with Basic Auth
Bola Idor in Hapi with Basic Auth — how this specific combination creates or exposes the vulnerability
In Hapi, a BOLA (Broken Level of Authorization) / IDOR occurs when an authenticated user can manipulate an object identifier (such as a numeric user ID or a UUID in the URL) and access or modify resources that belong to another user. Using HTTP Basic Auth in Hapi does not inherently prevent this: the server validates the credentials, establishes an identity (typically via request.auth.credentials), and then often proceeds to use user-supplied path or query parameters to locate a resource. If the server uses the authenticated identity to verify ownership but still trusts the user-supplied identifier, an attacker can change that identifier to target another user’s resource while the attacker’s identity remains their own account.
Consider a route like /api/users/{userId}/profile. After Basic Auth validation, Hapi might load the profile using the userId from the URL without confirming it matches the authenticated subject. Even if you derive the subject from credentials, failing to enforce a strict mapping between the authenticated subject and the resource identifier enables horizontal privilege escalation across user boundaries. Vertical escalation is also possible if role checks are applied to the resource lookup but the authorization guard is bypassed by tampering with the identifier (e.g., changing a role ID in the path or query).
OpenAPI specifications can unintentionally reinforce the issue if path parameters are described as mutable identifiers without clarifying the required relationship to the authenticated subject. During scanning, middleBrick’s OpenAPI/Swagger analysis resolves $ref definitions and cross-references runtime behavior; if the spec describes userId as an input without a security scheme tying it to the authenticated subject, the scan can flag a BOLA/IDOR risk. Runtime tests may observe that modifying the identifier changes the accessed resource while authentication remains valid, confirming a BOLA/IDOR finding under the Authentication and BOLA/IDOR checks.
Additionally, if Hapi plugins or custom route handlers expose secondary identifiers via query parameters (e.g., ?teamId=123) and those values are used to filter data without verifying membership, another variant of BOLA/IDOR emerges. The scan tests such vectors by probing multiple parameter combinations to detect insecure direct object references. Proper remediation requires tying every resource access to the authenticated subject rather than assuming the provided identifier is safe.
Basic Auth-Specific Remediation in Hapi — concrete code fixes
Remediation centers on ensuring that every resource access is validated against the authenticated subject derived from Basic Auth credentials. Do not trust path or query parameters alone; enforce an ownership or role mapping on the server side before reading, updating, or deleting data.
Example: Secure route implementation in Hapi
const Hapi = require('@hapi/hapi');
const validate = (request, h) => {
// Basic Auth credentials provided by the server (e.g., via hapi-auth-basic)
const credentials = request.auth.credentials;
return { isValid: true, credentials };
};
const init = async () => {
const server = Hapi.server({ port: 4000, host: 'localhost' });
server.auth.strategy('basic', 'basic', {
validate: (request, username, password, options) => {
// Replace with secure user lookup and password verification
const user = { id: 'u-123', username: 'alice', role: 'user' }; // example from DB
if (username === user.username && password === 'correct-hashed-password') {
return { credentials: user, isValid: true };
}
return { isValid: false };
}
});
server.auth.default('basic');
server.route({
method: 'GET',
path: '/api/users/{userId}/profile',
options: {
auth: 'basic',
handler: (request, h) => {
const authenticatedUser = request.auth.credentials;
const requestedUserId = request.params.userId;
// BOLA protection: ensure the authenticated user can only access their own profile
if (authenticatedUser.id !== requestedUserId) {
throw Boom.forbidden('You can only access your own profile');
}
// Fetch and return the profile safely
return { userId: requestedUserId, name: 'Alice' };
}
}
});
server.route({
method: 'PUT',
path: '/api/users/{userId}/profile',
options: {
auth: 'basic',
handler: (request, h) => {
const authenticatedUser = request.auth.credentials;
const requestedUserId = request.params.userId;
if (authenticatedUser.id !== requestedUserId) {
throw Boom.forbidden('You cannot update another user’s profile');
}
// Apply updates using authenticatedUser.id as the source of truth
// updateProfile(authenticatedUser.id, request.payload);
return { success: true };
}
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init().catch((err) => {
console.error(err);
});
Key points in the example:
- Authentication uses
hapi-auth-basic-style validate function that returns credentials containing a stable user identifier (e.g.,id). - Each handler compares
request.auth.credentials.idwith the path parameteruserIdand rejects mismatches. - Never use raw user input to construct database queries without this equality check.
For queries involving associations (e.g., team or organization resources), include an explicit membership check:
server.route({
method: 'GET',
path: '/api/teams/{teamId}/members',
options: {
auth: 'basic',
handler: async (request, h) => {
const user = request.auth.credentials;
const teamId = request.params.teamId;
// Verify membership: does the user belong to this team?
const isMember = await checkTeamMembership(user.id, teamId);
if (!isMember) {
throw Boom.forbidden('You are not a member of this team');
}
return listTeamMembers(teamId);
}
}
});
middleBrick can help identify these patterns by scanning unauthenticated attack surfaces and cross-referencing OpenAPI specs with runtime findings; its Authentication and BOLA/IDOR checks surface cases where identifiers are not properly bound to the authenticated subject.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |