Broken Access Control in Hapi with Basic Auth
Broken Access Control in Hapi with Basic Auth — how this specific combination creates or exposes the vulnerability
Broken Access Control occurs when authorization checks are missing, incomplete, or bypassed, allowing attackers to access resources or perform actions they should not. In Hapi, using HTTP Basic Auth without enforcing role-based or ownership checks can expose endpoints to BOLA/IDOR and privilege escalation. Basic Auth transmits credentials in an encoded (not encrypted) header unless protected by TLS, and if the server only validates authentication and skips authorization, any authenticated user may act on other users’ resources.
Consider a Hapi route that reads user profiles by ID with only Basic Auth validation but no check that the requesting user owns the target ID. An attacker can iterate numeric IDs and read other users’ data, a classic BOLA. Similarly, if a route accepts an identifier for update/delete without verifying that the authenticated user has the right to modify that specific resource, this becomes IDOR. Privilege escalation can occur if role claims or group memberships are not validated on each request, allowing a low-privilege user to invoke admin-only handlers.
With OpenAPI/Swagger analysis, definitions and $ref resolution help map which endpoints require which scopes or roles, but runtime checks must enforce them. middleBrick’s 12 security checks run in parallel and include BOLA/IDOR and Privilege Escalation, cross-referencing spec definitions with actual responses to highlight missing authorization logic in Hapi routes using Basic Auth.
Example vulnerable Hapi route with Basic Auth but no ownership check:
const Hapi = require('@hapi/hapi');
const bcrypt = require('bcrypt');
const validate = async (request, decoded) => {
// Only validates credentials, no role/ownership checks
return { isValid: true, credentials: { name: decoded.name } };
};
const init = async () => {
const server = Hapi.server({ port: 4000, host: 'localhost' });
server.auth.strategy('simple', 'basic', {
validate: validate,
password: 'secret' // In real use, use a proper credentials provider
});
// Vulnerable: No ownership or role check
server.route({
method: 'GET',
path: '/users/{id}',
options: {
auth: 'simple',
handler: async (request, h) => {
const userId = request.auth.credentials.name; // e.g., 'user1'
const targetId = request.params.id;
// Missing: ensure userId === targetId or user has proper role
return { data: `Profile data for user ${targetId}` };
}
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
In this example, any authenticated user can request /users/2 and potentially access another user’s profile if the server does not compare request.auth.credentials.name with request.params.id. Basic Auth provides authentication but does not enforce authorization; developers must add explicit checks.
Additionally, if role or scope claims are present in the decoded credentials, the server must validate them per endpoint. Without such checks, a low-privilege account might invoke handlers intended for admins, leading to privilege escalation. middleBrick’s BFLA/Privilege Escalation checks specifically test for these gaps by probing endpoints with different credential sets and inspecting whether actions or data are improperly permitted.
Remediation requires coupling authentication with authorization: verify resource ownership or enforce role-based access control on every route. Do not rely on Basic Auth alone to protect sensitive operations.
Basic Auth-Specific Remediation in Hapi — concrete code fixes
To secure Hapi routes using Basic Auth, move beyond simple credential validation and enforce ownership or role-based checks on each request. Below are concrete, working examples that address BOLA/IDOR and privilege escalation.
1) Enforce resource ownership by comparing authenticated user to requested ID:
const validate = async (request, decoded) => {
return { isValid: true, credentials: { name: decoded.name } };
};
server.route({
method: 'GET',
path: '/users/{id}',
options: {
auth: 'simple',
handler: async (request, h) => {
const userId = request.auth.credentials.name; // authenticated user identifier
const targetId = request.params.id;
if (userId !== targetId) {
throw Boom.unauthorized('You cannot access this resource');
}
return { data: `Profile data for user ${userId}` };
}
}
});
This ensures a user can only access their own profile, mitigating IDOR.
2) Use role-based checks for admin-only actions:
const validate = async (request, decoded) => {
// Assume decoded includes roles; in practice, fetch from a trusted source
const roles = decoded.roles || [];
return { isValid: true, credentials: { name: decoded.name, roles } };
};
server.route({
method: 'DELETE',
path: '/users/{id}',
options: {
auth: 'simple',
handler: async (request, h) => {
const { roles } = request.auth.credentials;
if (!roles.includes('admin')) {
throw Boom.forbidden('Admin privileges required');
}
const targetId = request.params.id;
// Proceed with delete for admins
return { message: `User ${targetId} deleted` };
}
}
});
Here, the handler explicitly verifies that the authenticated user has an admin role before allowing destructive operations, reducing privilege escalation risk.
3) Centralize authorization logic to avoid duplication and mistakes:
const canAccessUser = (authUser, targetId) => {
return authUser.name === targetId || authUser.roles.includes('admin');
};
server.route({
method: 'GET',
path: '/users/{id}',
options: {
auth: 'simple',
handler: async (request, h) => {
const userId = request.auth.credentials.name;
if (!canAccessUser(request.auth.credentials, request.params.id)) {
throw Boom.unauthorized('Access denied');
}
return { data: `Profile data for ${request.params.id}` };
}
}
});
By abstracting the policy into a function, you keep route handlers clean and ensure consistent enforcement. For production, replace the static password in the Basic Auth strategy with a secure credentials provider and always serve over HTTPS to protect the encoded credentials in transit.
These patterns align with OWASP API Top 10 controls for authentication and authorization and help satisfy requirements in frameworks such as PCI-DSS and SOC2. middleBrick’s scans can verify that such checks are present and flag endpoints where authorization is missing or inconsistent.