Bola Idor in Fiber with Api Keys
Bola Idor in Fiber with Api Keys — how this specific combination creates or exposes the vulnerability
Broken Object Level Authorization (BOLA) occurs when an API fails to enforce ownership or authorization checks between a user and a specific object identifier. Using API keys in a Fiber application can inadvertently enable BOLA when keys are treated as a global identity but the endpoints also expose object-specific identifiers without verifying that the authenticated key’s subject is allowed to access that object.
In Fiber, a typical pattern binds an API key to a user or client via middleware that sets a user context (e.g., ctx.claims) based on a validated key. If route handlers then use request parameters such as :id to access resources (e.g., /users/:id/profile) without confirming that the resource’s owning user identifier matches the key’s subject, BOLA arises. For example, an attacker can enumerate numeric IDs and read or modify another user’s profile because the handler trusts the ID alone rather than ensuring the ID maps to the authenticated key’s associated user.
Consider a route that retrieves a user’s settings by ID:
import { Router, Request, Response } from 'fiber';
router.get('/users/:id/settings', async (ctx) => {
const userId = ctx.params('id');
// BOLA: No check that userId matches the authenticated key’s subject
const settings = await db.userSettings.findUnique({ where: { userId } });
if (!settings) return ctx.status(404).send('Not found');
return ctx.json(settings);
});
Here, the API key middleware may set ctx.user, but the handler ignores it and directly trusts userId from the path. This enables horizontal BOLA: any authenticated client with a valid key can iterate over IDs to access others’ settings. Vertical privilege escalation can also occur if higher-privilege keys are mistakenly allowed to access objects they should not, such as admin-only resources, without explicit authorization checks.
OpenAPI specifications can unintentionally document object-level endpoints without clarifying authorization requirements, which can mislead developers. When combined with runtime behavior that does not validate ownership per request, the spec-to-runtime gap becomes a BOLA vector. The scanner’s BOLA/IDOR checks correlate spec-defined paths with runtime behavior to highlight such mismatches, emphasizing the need to bind keys to object ownership explicitly.
Api Keys-Specific Remediation in Fiber — concrete code fixes
To prevent BOLA when using API keys in Fiber, ensure every object-level operation validates that the authenticated key’s subject owns or is authorized for the target object. Below are concrete, safe patterns.
1) Enforce ownership checks using the authenticated key’s subject:
import { Router } from 'fiber';
router.get('/users/:id/settings', async (ctx) => {
const userId = ctx.params('id');
// Use the authenticated key’s subject, not the raw param
const subject = ctx.user.sub; // subject derived from API key claims
if (userId !== subject) {
return ctx.status(403).send('Forbidden: insufficient permissions');
}
const settings = await db.userSettings.findUnique({ where: { userId: subject } });
if (!settings) return ctx.status(404).send('Not found');
return ctx.json(settings);
});
This ensures that users can only access their own settings by comparing the path parameter to the key’s subject, mitigating horizontal BOLA.
2) For tenant-based or role-based access, implement an explicit authorization function:
async function canAccessUser(actor: { sub: string, roles: string[] }, targetId: string): Promise<boolean> {
// Admins can access any user; otherwise enforce ownership
if (actor.roles.includes('admin')) return true;
return actor.sub === targetId;
}
router.get('/users/:id', async (ctx) => {
const targetId = ctx.params('id');
const ok = await canAccessUser(ctx.user, targetId);
if (!ok) return ctx.status(403).send('Forbidden');
const user = await db.users.findUnique({ where: { id: targetId } });
return ctx.json(user);
});
3) Use route-level middleware to centralize ownership validation, reducing repetitive checks:
const authorizeResource = (resource: 'users' | 'orgs') => {
return async (ctx: Context) => {
const id = ctx.params('id');
const subject = ctx.user.sub;
// Example for users: ensure subject matches id
if (resource === 'users' && id !== subject) {
throw new Error('Forbidden');
}
// Attach validated entity for downstream handlers
ctx.state.entityId = id;
};
};
router.get('/users/:id/profile', authorizeResource('users'), async (ctx) => {
const userId = ctx.state.entityId as string;
const profile = await db.profiles.findUnique({ where: { userId } });
return ctx.json(profile);
});
4) When keys map to service identities rather than end users, scope keys to specific resources or scopes and validate scope permissions at runtime:
interface KeyClaims { scope: 'own' | 'admin', userId?: string }
router.delete('/records/:recordId', async (ctx) => {
const claims = ctx.user as KeyClaims;
const recordId = ctx.params('recordId');
if (claims.scope === 'own' && claims.userId !== recordId) {
return ctx.status(403).send('Forbidden: scope mismatch');
}
await db.records.delete({ where: { id: recordId } });
return ctx.status(204).send();
});
These patterns ensure API keys are used as authentication while explicit authorization checks enforce object-level permissions, effectively neutralizing BOLA/IDOR risks in Fiber applications.
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 |