Api Key Exposure in Hapi with Basic Auth
Api Key Exposure in Hapi with Basic Auth — how this specific combination creates or exposes the vulnerability
Hapi is a rich framework for building web services and APIs in Node.js. When Basic Authentication is used without additional protections, API keys or other bearer credentials can be inadvertently exposed through logging, error messages, or misconfigured routes. This combination becomes risky when developers store secrets in headers or cookies that Basic Auth does not inherently protect, and when responses include sensitive metadata that can be read by an attacker who has obtained network-level access or who can observe unencrypted traffic.
Basic Auth in Hapi relies on a username and password (or a token-like secret) transmitted in the Authorization header encoded as base64. While base64 is not encryption, developers sometimes mistakenly treat it as a safeguard for secrets such as API keys. If Hapi responses include the Authorization header or related values in logs, stack traces, or unsafe HTTP headers (e.g., via a debug plugin or custom logging strategy), an API key embedded in or alongside those credentials can be leaked to unauthorized parties.
Moreover, if Hapi services accept both Basic Auth and an API key (for example, passed as a custom header like x-api-key), the server might inadvertently echo the key in error responses, HTTP Warning headers, or diagnostic payloads. MiddleBrick’s unauthenticated scans detect scenarios where authentication is partially enforced but sensitive data can still be inferred from verbose error objects or misconfigured CORS settings. Even without direct access to server logs, an attacker can sometimes extract leaked keys via SSRF-induced interactions or by probing endpoints that return upstream configuration details.
The interplay of authentication mechanisms also matters. If rate limiting is not applied uniformly, an attacker can perform credential spraying against Basic Auth while monitoring for subtle timing differences or error messages that reference an API key. Similarly, if the API uses opaque tokens or session identifiers derived from the Basic Auth credentials, those tokens might be included in responses or logs, creating a secondary channel for key exposure. Hapi’s built-in validation and logging tools must be carefully tuned to avoid preserving sensitive material in structured data that could be retrieved later.
Because Hapi supports multiple authentication schemes side by side, developers must ensure that scopes, routes, and policies are consistent. A route protected by Basic Auth might still expose an API key if it proxies requests to an upstream service that returns secrets in JSON payloads or headers. MiddleBrick’s checks for Data Exposure and Unsafe Consumption are designed to surface such cross-channel leaks, especially when OpenAPI specs describe security schemes that do not match runtime behavior.
In practice, mitigating this risk requires strict separation of concerns: use Basic Auth only for identity verification, keep API keys in secure server-side stores, and ensure that no endpoint returns credentials or key material in any response body or header. Enforce Transport Layer Security to prevent on-path observation, and validate that error messages are generic. MiddleBrick’s LLM/AI Security checks further ensure that system prompts or generated documentation do not contain hardcoded keys that could be extracted via prompt injection techniques.
Basic Auth-Specific Remediation in Hapi — concrete code fixes
To secure Hapi APIs using Basic Auth while preventing API key exposure, implement strict separation between authentication and key management, and harden logging and error handling. Below are concrete, syntactically correct examples that demonstrate safe patterns.
Secure Hapi server with Basic Auth and isolated API key handling
const Hapi = require('@hapi/hapi');
const bcrypt = require('bcrypt');
const users = {
admin: {
username: 'admin',
password: await bcrypt.hash('StrongPassword123!', 10), // store hashed passwords
scope: 'admin'
}
};
const validate = async (request, username, password) => {
const user = users[username];
if (!user) return { isValid: false };
const isValid = await bcrypt.compare(password, user.password);
return { isValid, credentials: { username, scope: user.scope } };
};
const init = async () => {
const server = Hapi.server({
port: 443,
host: '0.0.0.0',
routes: {
cors: {
origin: ['https://trusted-frontend.com'],
additionalHeaders: ['authorization', 'content-type']
},
validate: {
failAction: 'log' // avoid returning detailed errors to clients
}
}
});
server.auth.strategy('basic-auth', 'basic', {
validate: validate,
header: 'authorization',
realm: 'api-realm',
password: 'a-very-long-and-secure-server-secret-used-for-hashing-nonce' // used by hapi's cryptic hashing utilities
});
server.auth.default('basic-auth');
// Example route that requires auth but does NOT expose API keys
server.route({
method: 'GET',
path: '/public-data',
options: {
auth: 'basic-auth',
handler: (request, h) => {
// API key should be retrieved server-side from a secure vault, never from request
const apiKey = process.env.API_KEY_VAULT; // injected at runtime
return { data: 'safe payload', authenticatedAs: request.auth.credentials.username };
},
plugins: {
'hapi-auth-cookie': null // avoid mixing cookie-based leakage
}
}
});
// Centralized error handler to prevent leaking stack traces or keys
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
// Log full details internally; return generic message
console.error('Auth error:', response.message);
return h.response({ error: 'Unauthorized' }).code(401).takeover();
}
return h.continue;
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init().catch(err => {
console.error(err);
process.exit(1);
});
Key remediation practices
- Never echo the Authorization header or API keys in responses, logs, or error payloads.
- Use environment variables or a secrets manager to store API keys; inject them at runtime rather than passing through HTTP headers.
- Set
failAction: 'log'in validation options to avoid returning detailed errors that could aid attackers. - Enforce HTTPS with strong TLS configurations to prevent on-path interception of Basic Auth credentials.
- Apply consistent CORS policies and avoid exposing debug routes in production.
- Regularly rotate credentials and API keys, and scope permissions to the minimum required.
By combining these patterns, Hapi services can use Basic Auth for identity checks while keeping API keys protected server-side, reducing the risk of inadvertent exposure through logging, errors, or cross-mechanism interactions.