Cors Wildcard in Hapi with Mutual Tls
Cors Wildcard in Hapi with Mutual Tls — how this specific combination creates or exposes the vulnerability
A CORS wildcard (Access-Control-Allow-Origin: *) combined with Mutual TLS (mTLS) in Hapi can unintentionally broaden the effective attack surface. CORS is a browser-enforced mechanism; the wildcard allows any origin to read the response, provided the server also includes the CORS headers. When mTLS is enabled, the server validates client certificates, but the CORS policy is evaluated after TLS termination and after certificate validation in most deployments. This means a request with a valid client certificate can still receive a response that a browser would expose to any page on any origin.
In Hapi, if the CORS configuration uses origin: ['*'] while requiring client certificates, the server may still issue credentials (cookies, authorization headers) to authenticated mTLS clients. A malicious site can then make cross-origin requests from a browser, leveraging the valid mTLS session to access sensitive endpoints, effectively bypassing origin-based isolation. This is particularly risky when endpoints return sensitive user data or tokens, because the browser will permit the embedded site to read the response despite mTLS proving client identity to the server.
Consider an endpoint like /api/v1/patient/me that returns protected health information. With an mTLS-secured Hapi server and a CORS wildcard, any page loaded in the browser can initiate a request with a valid client certificate (if the user possesses the cert) and read the data. Attack patterns such as CSRF or malicious browser extensions can leverage this setup to exfiltrate data across origins. This intersects with OWASP API Top 10 controls around security misconfiguration and insufficient authorization, and may map to compliance considerations in PCI-DSS and HIPAA where data exposure across trust boundaries must be minimized.
Mutual Tls-Specific Remediation in Hapi — concrete code fixes
Remediation centers on tightening CORS origins and ensuring mTLS is enforced consistently. Avoid broad wildcard origins when mTLS is in use; instead, specify exact origins and, where necessary, include credentials only for trusted origins. Below are concrete Hapi examples that combine mTLS setup with a restricted CORS policy.
Example 1: Hapi server with mTLS and strict CORS
const Hapi = require('@hapi/hapi');
const fs = require('fs');
const init = async () => {
const server = Hapi.server({
port: 443,
host: '0.0.0.0',
tls: {
key: fs.readFileSync('/path/to/server.key'),
cert: fs.readFileSync('/path/to/server.crt'),
ca: [fs.readFileSync('/path/to/ca.crt')],
requestCert: true,
rejectUnauthorized: true
}
});
// Strict CORS: specific origins, credentials allowed only for trusted origins
await server.register({
plugin: require('@hapi/c inert'),
options: {
policies: [
{
origins: ['https://app.example.com', 'https://admin.example.com'],
headers: true,
exposedHeaders: true,
credentials: true
}
]
}
});
server.route({
method: 'GET',
path: '/api/v1/patient/me',
options: {
auth: 'tls-certificate',
cors: {
origin: ['https://app.example.com'],
additionalHeaders: ['x-custom-header'],
exposedHeaders: ['x-ratelimit-limit']
}
},
handler: (request, h) => {
// request.auth.credentials contains parsed client certificate fields
return { patientId: request.auth.credentials.subject.cn };
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init().catch(err => {
console.error(err);
process.exit(1);
});
Example 2: Centralized CORS and mTLS validation logic
const Hapi = require('@hapi/hapi');
const tlsUtils = require('./tls-utils'); // custom helper to extract cert fields
const server = Hapi.server({
port: 443,
host: '0.0.0.0',
tls: {
key: fs.readFileSync('/path/to/server.key'),
cert: fs.readFileSync('/path/to/server.crt'),
ca: [fs.readFileSync('/path/to/ca.crt')],
requestCert: true,
rejectUnauthorized: true
}
});
// Reusable CORS options for multiple routes
const corsOptions = {
origin: ['https://partners.example.com'],
credentials: true,
additionalHeaders: ['x-request-id'],
exposedHeaders: ['x-ratelimit-remaining']
};
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
// Optionally tighten headers for errors
response.output.headers['Access-Control-Allow-Origin'] = corsOptions.origin[0];
}
return h.continue;
});
server.route({
method: 'POST',
path: '/api/v1/orders',
options: {
auth: {
mode: 'required',
strategy: 'tls-certificate'
},
cors: corsOptions
},
handler: (request, h) => {
const subject = tlsUtils.getSubject(request);
return h.response({ orderId: 'generated-id' }).code(201);
}
});
const start = async () => {
await server.register(require('@hapi/cors'));
await server.start();
console.log('Secure server listening on', server.info.uri);
};
start();
Key practices
- Never use
origin: ['*']when mTLS is enforced; replace with explicit origin lists. - Validate client certificates with
requestCert: trueandrejectUnauthorized: trueto ensure only trusted clients can authenticate. - Map certificate fields (e.g., subject CN) to authorization decisions within handlers, avoiding reliance solely on mTLS for fine-grained permissions.
- Combine CORS settings with route-level auth and avoid broad
exposedHeadersthat could leak sensitive metadata.
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 |
Frequently Asked Questions
Does a CORS wildcard always break mTLS security?
How can I test my Hapi CORS and mTLS configuration?
Access-Control-Allow-Origin and confirm they are not * when credentials are required. You can also run middleBrick scans against your endpoint to surface CORS misconfigurations alongside mTLS requirements.