Data Exposure in Hapi (Javascript)
Data Exposure in Hapi with Javascript — how this specific combination creates or exposes the vulnerability
Hapi is a rich-featured web framework for Node.js, and when used with JavaScript it can inadvertently expose sensitive data through misconfigured routes, serializers, or error handling. Data Exposure in this context refers to the risk that responses return more information than intended — such as internal fields, stack traces, or sensitive payloads — enabling attackers to infer behavior, harvest PII, or prepare further attacks.
One common pattern in Hapi is attaching raw database or service objects directly to the response. Because JavaScript is dynamically typed, it is easy to pass an object containing passwords, tokens, or internal IDs without pruning or transforming it. For example, returning a user record from a database query without removing sensitive keys results in Data Exposure:
const Hapi = require('@hapi/hapi');
const init = async () => {
const server = Hapi.server({ port: 4000 });
server.route({
method: 'GET',
path: '/users/{id}',
handler: async (request, h) => {
const userId = request.params.id;
// Vulnerable: returning full user object including password hash and internal fields
const user = await db('users').where({ id: userId }).first();
return user; // risk: exposes sensitive fields in the response
}
});
await server.start();
};
init();
In JavaScript, it is also common to rely on automatic JSON serialization. If an object contains methods, circular references, or sensitive runtime properties, these may be serialized unless explicitly filtered. Hapi’s default JSON response behavior will include all enumerable properties, which may unintentionally surface internal state:
const server = Hapi.server({ port: 5000 });
server.route({
method: 'GET',
path: '/profile',
handler: (request, h) => {
const profile = {
username: 'alice',
email: '[email protected]',
_internalToken: 'abc-123',
toJSON() {
// If toJSON is omitted or incomplete, _internalToken may leak
return { username: this.username, email: this.email };
}
};
return profile;
}
});
Error handling is another vector. In JavaScript, unhandled exceptions or improperly caught errors can return stack traces or environment details. Hapi’s default error responses may include stack traces in development mode, which in JavaScript are rich sources of internal paths and variable names:
server.route({
method: 'GET',
path: '/data',
handler: (request, h) => {
// If this throws, Hapi may return a detailed error in some configurations
const a = someUndefinedFunction();
return a;
}
});
Additionally, JavaScript’s flexibility with object references and mutation can lead to Data Exposure when caching or logging responses. If response objects are cached or logged in their entirety, sensitive fields remain persisted in logs or intermediary stores. This is especially risky when using global or module-level variables in JavaScript to store request-scoped data.
Finally, serialization settings in Hapi’s response tools must be carefully tuned. The framework allows customizing JSON.stringify behavior; without explicitly revoking sensitive keys, you risk Data Exposure across all endpoints that rely on default serialization.
Javascript-Specific Remediation in Hapi — concrete code fixes
To mitigate Data Exposure in Hapi with JavaScript, adopt explicit response shaping, strict serialization controls, and defensive error handling. The goal is to ensure only intended data leaves the server, regardless of JavaScript’s dynamic nature.
First, explicitly pick the fields you intend to return instead of passing raw objects. Use a mapping or a dedicated transform layer to remove sensitive keys:
server.route({
method: 'GET',
path: '/users/{id}',
handler: async (request, h) => {
const userId = request.params.id;
const user = await db('users').where({ id: userId }).first();
// Explicitly return only safe fields
return {
id: user.id,
username: user.username,
email: user.email,
createdAt: user.created_at
};
}
});
Second, define a stable toJSON method on objects that must be serialized, ensuring sensitive properties are omitted consistently across responses:
function toSafeUser(user) {
return {
id: user.id,
username: user.username,
email: user.email
// intentionally excluding passwordHash, tokens, internalId
};
}
server.route({
method: 'GET',
path: '/users/{id}',
handler: async (request, h) => {
const user = await db('users').where({ id: request.params.id }).first();
return toSafeUser(user);
}
});
Third, configure Hapi to limit error exposure. Use domain-based error mapping and avoid returning stack traces in production. In JavaScript, catch errors locally and return generic messages while logging details securely:
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
// Do not expose stack in production
const safeResponse = {
statusCode: response.output.statusCode,
error: response.output.payload.error,
message: response.output.payload.message
};
return h.response(safeResponse).code(response.output.statusCode);
}
return h.continue;
});
Fourth, freeze or deep-copy objects before using them in contexts where JavaScript’s reference semantics might cause unintended sharing. This prevents accidental mutation or leakage via cached references:
const safeData = JSON.parse(JSON.stringify(raw)); // simple deep copy for JSON-safe data
return safeData;
Finally, review all route configurations and plugins for default serialization behaviors. Combine these practices with regular scans using tools like middleBrick to validate that Data Exposure is reduced and that findings align with remediation guidance:
- Use the middleBrick CLI to scan from terminal with
middlebrick scan <url>and verify your endpoints do not leak sensitive fields. - Integrate the GitHub Action to add API security checks to your CI/CD pipeline, failing builds if risk scores exceed your threshold.
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 |