Insecure Direct Object Reference in Express with Bearer Tokens
Insecure Direct Object Reference in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Insecure Direct Object Reference (BOLA/IDOR) in Express when Bearer Tokens are used for authorization occurs when an API endpoint exposes a direct reference (such as a numeric ID or UUID) in the URL and does not verify that the authenticated subject associated with the Bearer Token is allowed to access that specific object. The presence of a Bearer Token often creates a false sense of full authorization: the token may identify a user or client, but if the endpoint only checks that a token exists and then directly uses user-supplied identifiers to query a database, an attacker can tamper with the identifier to access other users' resources.
Consider an Express route like /api/users/:userId/profile. If the route reads req.params.userId and uses it to look up a profile without confirming that the profile belongs to the subject identified by the Bearer Token, the endpoint is vulnerable. An attacker with a valid Bearer Token for their own account can change :userId to another user's ID and read or manipulate data. This is a BOLA/IDOR issue because the authorization decision is based solely on object ownership or capabilities rather than the token's associated permissions or tenant context.
In a typical compromised flow, the attacker intercepts or knows a valid Bearer Token (e.g., from a compromised session or a low-privilege account), then iterates over plausible IDs. Because the API trusts the ID and does not enforce scoped access — such as ensuring the requested resource matches the token's associated organization or user — the server returns data it should have withheld. This can expose personal data, internal records, or administrative objects. The vulnerability is not about token validity; it is about missing ownership or tenant checks between the authenticated subject and the referenced resource.
OpenAPI specifications can unintentionally reinforce the risk if path parameters and request bodies expose object references without describing authorization constraints. When combined with runtime behavior that does not cross-check the token's scopes or roles against the referenced object, the API surface widens. For example, a route that allows GET /organizations/:orgId/members must verify that the Bearer Token's subject has explicit membership or admin rights for the supplied orgId, rather than assuming the ID is safe because the request includes a token.
Real-world attack patterns include enumeration of IDs when responses differ between allowed and forbidden resources, and horizontal privilege escalation where a user accesses another user's data within the same tenant. In regulated contexts, such BOLA/IDOR issues can map to violations of confidentiality and access control requirements in frameworks like OWASP API Top 10 and SOC2 controls. Detection often involves correlating runtime requests (where object references are used) with authorization logic to ensure every direct object reference is validated against the authenticated subject's permissions.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
To remediate BOLA/IDOR in Express with Bearer Tokens, enforce that every access to a direct object reference is validated against the subject derived from the token. Below are concrete, idiomatic code examples that demonstrate secure patterns.
Example 1: Validate object ownership before access
Assume a JWT Bearer Token contains a sub claim identifying the user. The route should ensure that the requested resource belongs to that subject.
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
// Middleware to extract and attach user from Bearer Token
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = authHeader.slice(7);
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.user = { subject: payload.sub, roles: payload.roles || [] };
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
app.get('/api/users/:userId/profile', authMiddleware, async (req, res) => {
const requestedUserId = req.params.userId;
const currentUserId = req.user.subject;
// Enforce ownership: subject must match the requested user ID
if (requestedUserId !== currentUserId) {
return res.status(403).json({ error: 'Forbidden: access to this profile denied' });
}
const profile = await db.getProfileByUserId(requestedUserId);
if (!profile) {
return res.status(404).json({ error: 'Not found' });
}
res.json(profile);
});
Example 2: Scoped access for organization resources
For endpoints that reference organization-scoped objects, verify that the token's subject has the necessary role or membership for the supplied organization ID.
async function userHasAccessToOrg(userId, orgId) {
// Check membership table or role claim
return db.userInOrganization(userId, orgId);
}
app.get('/api/organizations/:orgId/members', authMiddleware, async (req, res) => {
const { orgId } = req.params;
const userId = req.user.subject;
if (!await userHasAccessToOrg(userId, orgId)) {
return res.status(403).json({ error: 'Forbidden: insufficient permissions for organization' });
}
const members = await db.getMembersByOrg(orgId);
res.json(members);
});
General mitigation checklist for Express + Bearer Tokens
- Always extract the subject from the token and never rely solely on object identifiers.
- Apply per-route checks that the subject has permission for the specific referenced object (ownership, role, or tenant).
- Use parameterized queries or an ORM to avoid injection, but remember that injection prevention is separate from BOLA/IDOR prevention.
- Ensure tokens carry sufficient claims (e.g., roles, org memberships) to make authorization decisions without extra lookups where possible.
- Return consistent error messages (e.g., 403) for both missing resources and unauthorized access to avoid leaking existence via response differences.
Tools like the middleBrick CLI can scan endpoints described in an OpenAPI spec to surface potential BOLA/IDOR findings. For teams seeking continuous visibility, the Pro plan’s GitHub Action can integrate checks into CI/CD pipelines, while the Dashboard helps track scores and remediation progress over time.
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 |
Frequently Asked Questions
Why does a valid Bearer Token not prevent IDOR in Express APIs?
How can I test my Express endpoints for BOLA/IDOR without a pentest vendor?
middlebrick scan <your-api-url>. It tests unauthenticated attack surface and authenticated scenarios where provided, returning prioritized findings with severity and remediation guidance. For continuous checks, add the GitHub Action to fail builds if risk scores drop below your chosen threshold.