Cors Wildcard in Hapi with Api Keys
Cors Wildcard in Hapi with Api Keys — how this specific combination creates or exposes the vulnerability
A CORS wildcard (Access-Control-Allow-Origin: *) combined with API key authentication in a Hapi server can unintentionally expose protected endpoints to any origin. When credentials or authorization are required, browsers enforce that Access-Control-Allow-Origin cannot be a wildcard if withCredentials is true. However, many clients send the API key in headers or cookies while relying on a wildcard origin, which can allow a malicious site to make authenticated requests on behalf of a user if the API key is stored in a cookie or if the endpoint does not validate the Origin header properly.
Hapi’s routing and CORS configuration can inadvertently permit cross-origin requests from any domain when a wildcard is used, even when API keys are required. For example, if an endpoint uses an API key via a header (e.g., x-api-key) and the server responds with *, a compromised site can include that header if the browser allows it or if the API key is leaked via logs, referrer, or insecure storage. This can lead to unauthorized access to user data or operations, violating the same-origin policy intent and enabling CSRF-like scenarios where an authenticated request is initiated from an untrusted origin.
In the context of middleBrick’s security checks, this combination is flagged because the scanner tests whether CORS headers expose an unauthenticated attack surface and whether API key requirements are enforced across origins. If the wildcard is present, the scan may find that preflight responses allow any origin, which can be chained with API key misplacement (e.g., keys in query strings or exposed in client-side code) to create a path for unauthorized access. The scan checks for Security Misconfiguration and validates that authentication mechanisms are not undermined by permissive CORS policies.
For reference, an unsafe Hapi CORS setup might look like this:
const Hapi = require('@hapi/hapi');
const cors = {
origin: ['*'],
additionalHeaders: ['x-api-key']
};
const init = async () => {
const server = Hapi.server({ port: 4000, host: 'localhost' });
server.route({
method: 'GET',
path: '/secure',
options: {
cors: cors,
handler: (request, h) => {
const apiKey = request.headers['x-api-key'];
if (apiKey !== 's3cr3t') {
throw Boom.unauthorized('Invalid API Key')
}
return { status: 'ok' };
}
}
});
await server.register(require('@hapi/cors'));
await server.start();
};
init();
In this example, the wildcard origin permits any domain to receive the response headers, including the x-api-key header in preflight requests. Even though the handler validates the key, the CORS policy may allow an attacker to trigger requests from a malicious page if the key is inadvertently accepted from the request (e.g., via a proxy or misconfigured gateway). Proper remediation involves tightening the CORS origin to known domains and ensuring API keys are validated server-side without relying on CORS for enforcement.
Api Keys-Specific Remediation in Hapi — concrete code fixes
To secure a Hapi API that uses API keys, the CORS policy must specify explicit origins and avoid wildcards when credentials or sensitive headers are involved. Additionally, API keys should be validated before routing logic, using server extensions or route pre-handlers to ensure consistent enforcement. Keys should be transmitted via headers (not query strings) and stored securely on the client side.
Below is a hardened Hapi example that restricts CORS to known origins, validates the API key in a route pre-handler, and sets appropriate security headers:
const Hapi = require('@hapi/hapi');
const cors = {
origin: ['https://app.example.com', 'https://admin.example.com'],
additionalHeaders: ['x-api-key'],
corsHeaders: {
'access-control-allow-origin': 'https://app.example.com',
'access-control-allow-headers': 'x-api-key'
}
};
const validateApiKey = (request, h) => {
const apiKey = request.headers['x-api-key'];
if (!apiKey || apiKey !== process.env.API_KEY) {
return h.response({ error: 'Forbidden' }).code(403);
}
return h.continue;
};
const init = async () => {
const server = Hapi.server({ port: 4000, host: 'localhost' });
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
return h.response({ error: 'Unauthorized' }).code(401);
}
return h.continue;
});
server.route({
method: 'GET',
path: '/secure',
options: {
cors: cors,
pre: [
{ method: validateApiKey, assign: 'apiKeyValid' }
],
handler: (request, h) => {
return { status: 'authorized', message: 'Access granted' };
}
}
});
await server.register(require('@hapi/cors'));
await server.start();
};
init();
This approach ensures that only requests from approved origins with a valid API key reach the handler. The CORS configuration explicitly lists allowed origins and headers, reducing the risk of wildcard misuse. For broader enforcement across many routes, consider using a server extension or a dedicated authentication strategy that integrates with your API key management system. With middleBrick’s scans, you can verify that CORS headers do not expose sensitive endpoints to arbitrary origins and that API key validation is consistently applied.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |