Bola Idor in Koa with Mutual Tls
Bola Idor in Koa with Mutual Tls — how this combination creates or exposes the vulnerability
Broken Object Level Authorization (BOLA) is an API security weakness where an attacker can access or modify objects they should not be allowed to, typically by manipulating object identifiers such as IDs in URLs or parameters. In a Koa application, BOLA often manifests when object-level access checks are incomplete or incorrectly applied after authentication/authorization logic. Using Mutual TLS (mTLS) in this context introduces a nuanced interaction: mTLS provides strong client authentication at the transport layer, which can lead developers to assume that identity and authorization are handled securely. However, mTLS does not enforce object-level permissions; it only confirms that the connecting client is valid and trusted.
When mTLS is used in Koa, the server may extract client identity from the certificate (e.g., subject or serial number) and then load user-specific data. If the application then uses user-supplied object IDs without verifying that the object belongs to the authenticated client, BOLA occurs. For example, an endpoint like /api/users/:userId/profile might verify the client certificate to identify the user, but if the route uses userId from the URL directly to fetch a profile without confirming that the profile belongs to that user, an attacker can change userId to access another user’s profile. This is a classic horizontal BOLA. Vertical BOLA can also occur when a client with a lower privilege certificate (e.g., a standard user mTLS cert) is able to access administrative object endpoints because the server does not re-check privilege boundaries after mTLS authentication.
Another subtlety is that mTLS is often terminated at a gateway or load balancer. If the Koa application receives requests over plain HTTP after TLS termination and relies on headers (like custom headers or forwarded certificates) to infer client identity, misconfiguration can cause identity confusion. An attacker might manipulate headers to impersonate another client, and if object IDs are not scoped to the authenticated identity, BOLA becomes exploitable. The risk is compounded when the application uses opaque identifiers that are predictable or sequential, making enumeration feasible. In such setups, mTLS ensures a trusted channel but does not prevent insecure object access logic, so developers must explicitly enforce object ownership checks within the application layer.
Mutual Tls-Specific Remediation in Koa — concrete code fixes
To mitigate BOLA in Koa while using mTLS, you must combine proper certificate handling with strict object-level authorization. Below are concrete, working examples that demonstrate how to implement these controls correctly.
1. Configure mTLS in Koa and extract client identity
Ensure your Koa server is set up to require and validate client certificates. Use the ssl options to provide the CA certificate that signs client certs, and parse the client certificate from the request.
const https = require('https');
const fs = require('fs');
const Koa = require('koa');
const app = new Koa();
const serverOptions = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
ca: fs.readFileSync('ca-cert.pem'),
requestCert: true,
rejectUnauthorized: true
};
https.createServer(serverOptions, app.callback()).listen(3000);
Then, in your route handlers, extract the client certificate and derive a trusted identity. Avoid trusting arbitrary headers that could be spoofed in a non-mTLS context.
app.use(async (ctx, next) => {
const cert = ctx.req.clientCertificate;
if (!cert || !cert.subject) {
ctx.throw(401, 'Client certificate required');
}
// Use a stable identifier from the certificate, e.g., serial or CN
const clientId = cert.subject.CN; // or cert.serialNumber
ctx.state.client = { id: clientId, cert };
await next();
});
2. Enforce object-level authorization relative to the authenticated client
After identifying the client, ensure that any object access is scoped to that client. For example, when fetching or modifying a user profile, verify that the profile’s owner matches the client identity derived from the certificate.
app.use(async (ctx, next) => {
await next();
// Example: /api/users/:userId/profile
const requestedUserId = ctx.params.userId;
const authenticatedUserId = ctx.state.client.id;
if (requestedUserId !== authenticatedUserId) {
ctx.throw(403, 'Forbidden: cannot access other user resources');
}
});
For data stores, always filter queries by the authenticated client ID. If using an ORM or database client, incorporate the client ID into every relevant query to prevent horizontal BOLA.
// Example with a generic database client
async function getUserProfile(userId, clientId) {
const result = await db.query('SELECT * FROM profiles WHERE user_id = $1 AND owner_id = $2', [userId, clientId]);
if (!result.rows.length) {
throw new Error('Not found or access denied');
}
return result.rows[0];
}
3. Handle mTLS termination scenarios carefully
If your Koa app sits behind a proxy that terminates mTLS, configure it to only accept connections from trusted proxies and to require that the proxy sets a verified header or preserves the original certificate information. Never rely on unverified headers for authentication.
app.use(async (ctx, next) => {
const clientCert = ctx.get('x-client-cert'); // Only if proxy injects and signs it
if (!clientCert) {
ctx.throw(401, 'Client identity missing');
}
// Validate certificate format or signature if needed
ctx.state.client = { id: extractClientIdFromCert(clientCert) };
await next();
});
By combining mTLS client validation with explicit object ownership checks, you reduce the risk of BOLA while preserving the security benefits of mutual authentication.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |