Bola Idor in Restify with Bearer Tokens
Bola Idor in Restify with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Broken Object Level Authorization (BOLA) occurs when an API exposes endpoints that identify resources solely by user-supplied identifiers without verifying that the requesting user owns or is authorized to access that specific resource. In a Restify service that uses Bearer Tokens for authentication, BOLA can manifest when endpoints accept an identifier (e.g., a user ID or record ID) in the URL or query parameters and rely only on the presence of a valid token, rather than tying the token to the requested resource.
Consider a Restify endpoint designed to fetch a user profile by ID:
server.get('/users/:id', (req, res, next) => {
const userId = req.params.id;
const tokenPayload = req.user; // from Bearer token validation middleware
// Missing: check that tokenPayload.sub equals userId
return res.send(db.getUser(userId));
});
If the Bearer token is validated and decoded into req.user, but the server does not enforce that req.user.sub (or a similar claim) matches the :id in the path, an attacker can change the ID to any other user's identifier and access or modify data they should not see or control. This is a classic BOLA: the authorization boundary is missing, even though authentication via Bearer Token is present.
In Restify, this risk is heightened when developers use opaque IDs that are not predictable, because they might assume obscurity protects them, but without an explicit ownership check, attackers can iterate through IDs or use known relationships to escalate access. Attack patterns such as IDOR (a subset of BOLA) are common in REST APIs where endpoints like /accounts/{accountId} or /projects/{projectId}/members do not validate that the authenticated principal (from the Bearer token) is a member of the requested resource.
Another scenario involves nested resources. For example, an endpoint like /organizations/:orgId/users/:userId might validate the Bearer token but fail to ensure the authenticated user has rights to the organization orgId before exposing or modifying userId. Because Restify does not enforce authorization semantics by default, this combination of route parameters and bearer-only authentication creates a path for horizontal or vertical privilege escalation depending on the relationship between users and organizations.
Real-world attack chains often include reconnaissance to discover IDs (e.g., through public information or enumeration), followed by crafted requests that swap IDs in the URL while keeping a valid Bearer Token. Because the token proves identity but not authorization for the specific object, the request succeeds from the server’s perspective, leading to unauthorized reads or modifications. This is why BOLA is frequently listed in the OWASP API Security Top 10 and maps to violations of the principle of least privilege.
Bearer Tokens-Specific Remediation in Restify — concrete code fixes
To mitigate BOLA when using Bearer Tokens in Restify, you must couple authentication (token validation) with explicit ownership or scope checks on every request that accesses a user-controlled resource. Below are concrete, idiomatic fixes you can apply in route handlers.
1) Always compare a subject claim from the token with the resource identifier. For example, if your JWT includes a sub claim representing the user ID:
server.get('/users/:id', (req, res, next) => {
const userId = req.params.id;
const tokenPayload = req.user; // validated Bearer token payload
if (!tokenPayload || tokenPayload.sub !== userId) {
return next(new UnauthorizedError('You can only access your own profile.'));
}
return res.send(db.getUser(userId));
});
This ensures that even with a valid Bearer Token, a user cannot request another user’s profile by changing the :id path parameter.
2) For organization-scoped endpoints, verify membership or role before allowing access:
server.get('/organizations/:orgId/members/:memberId', async (req, res, next) => {
const { orgId, memberId } = req.params;
const tokenPayload = req.user;
if (!tokenPayload) {
return next(new UnauthorizedError('Missing token.'));
}
const isMember = await db.isOrganizationMember(orgId, tokenPayload.sub);
if (!isMember) {
return next(new UnauthorizedError('You do not have access to this organization.'));
}
// Optionally also enforce memberId matches tokenPayload.sub for direct member endpoints
return res.send({ orgId, memberId });
});
3) Use middleware to centralize ownership checks when many routes share the same pattern. In Restify, you can create a helper that binds the token’s subject to the request and validates against route parameters:
function ensureResourceOwnership(req, res, next) {
const resourceId = req.params.id; // or req.params.orgId, etc.
const userId = req.user && req.user.sub;
if (!userId || userId !== resourceId) {
return next(new UnauthorizedError('Forbidden: insufficient ownership.'));
}
return next();
}
server.get('/users/:id', ensureResourceOwnership, (req, res, next) => {
return res.send(db.getUser(req.params.id));
});
4) Apply role- or scope-based checks when the token includes scopes or roles. For example, an admin scope may bypass strict ownership checks, but a regular user must match IDs:
server.del('/users/:id', (req, res, next) => {
const userId = req.params.id;
const tokenPayload = req.user;
if (!tokenPayload) {
return next(new UnauthorizedError('Unauthorized.'));
}
const canDelete = tokenPayload.scopes.includes('admin:users') || tokenPayload.sub === userId;
if (!canDelete) {
return next(new UnauthorizedError('Cannot delete other users.'));
}
db.deleteUser(userId);
return res.send(204);
});
5) When IDs are not predictable, still enforce ownership — obscurity is not a substitute for authorization. Use the token’s subject to perform a lookup by owner rather than by ID alone, or join the resource table with a user table via a foreign key that references the subject claim.
By combining Bearer Token validation with explicit checks that the authenticated subject maps to the resource being accessed, you close the BOLA vector. This approach aligns with least-privilege principles and maps cleanly to findings in security scans that flag missing authorization boundaries.
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 |