HIGH insecure direct object referenceexpressbasic auth

Insecure Direct Object Reference in Express with Basic Auth

Insecure Direct Object Reference in Express with Basic Auth — how this specific combination creates or exposes the vulnerability

Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal object references—such as database IDs, file paths, or sequential keys—and allows an attacker to manipulate those references to access unauthorized data. In Express, this commonly appears in routes like /users/:id or /api/invoices/:invoiceId where the identifier is taken directly from the request without confirming that the requesting user has the right to view or modify that specific object.

Combining Basic Auth with IDOR introduces a subtle but significant risk. Basic Auth sends a base64-encoded username:password string in the Authorization header; it does not inherently scope or partition data. A server may authenticate the user (valid credentials), but then fail to enforce authorization checks tied to that authenticated identity. For example, if the route uses the authenticated username for some logging but still allows req.params.id to reference any resource, an attacker can iterate over IDs and read or modify data belonging to other users. Because Basic Auth lacks built-in scopes or tenant boundaries, the burden falls entirely on the application to enforce ownership or role checks, and missing checks directly enable BOLA/IDOR.

Consider an Express route that retrieves a user’s profile by ID:

app.get('/api/users/:id', (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }
  const base64 = authHeader.split(' ')[1];
  const decoded = Buffer.from(base64, 'base64').toString('utf-8');
  const [username, password] = decoded.split(':');
  // Naive credential check (in real apps, use hashed passwords and constant-time compare)
  if (!isValidUser(username, password)) {
    return res.status(401).send('Unauthorized');
  }
  const userId = req.params.id;
  const userData = db.getUserById(userId);
  if (!userData) {
    return res.status(404).send('Not found');
  }
  res.json(userData);
});

In this example, authentication is performed, but authorization is absent: userId from the URL is used directly without verifying that it belongs to the authenticated username. An attacker who knows another valid user’s ID can access that data, resulting in an IDOR. The same pattern applies to resources such as invoices, documents, or configurations where the identifier is user-controlled but not bound to the authenticated identity.

middleBrick scans detect this class of issue by correlating authentication findings (Basic Auth usage) with unvalidated object references in route parameters. It checks whether the application uses the authenticated subject (e.g., username or mapped tenant) to constrain data access and highlights missing ownership or scope checks as high-severity findings aligned with OWASP API Top 10 (2023) A01: Broken Object Level Authorization.

Basic Auth-Specific Remediation in Express — concrete code fixes

Remediation centers on ensuring that after authenticating a user via Basic Auth, every data access uses the authenticated subject to enforce ownership or tenant boundaries. Do not rely on obscurity or sequential IDs; instead, bind the resource to the authenticated identity and validate on each request.

1) Enforce ownership by comparing the authenticated username with the resource owner before returning data:

app.get('/api/users/:id', (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }
  const base64 = authHeader.split(' ')[1];
  const decoded = Buffer.from(base64, 'base64').toString('utf-8');
  const [username, password] = decoded.split(':');
  if (!isValidUser(username, password)) {
    return res.status(401).send('Unauthorized');
  }
  const userId = req.params.id;
  // Ensure the requested user ID matches the authenticated username
  if (String(userId) !== username) {
    return res.status(403).send('Forbidden: insufficient scope');
  }
  const userData = db.getUserById(userId);
  if (!userData) {
    return res.status(404).send('Not found');
  }
  res.json(userData);
});

2) For resources owned by a user but keyed by an internal numeric ID, first look up the resource, then verify ownership via the authenticated subject:

app.get('/api/invoices/:invoiceId', (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }
  const base64 = authHeader.split(' ')[1];
  const decoded = Buffer.from(base64, 'base64').toString('utf-8');
  const [username] = decoded.split(':');
  if (!isValidUser(username, '')) { // passwordless check for demo; use proper validation in prod
    return res.status(401).send('Unauthorized');
  }
  const invoiceId = req.params.invoiceId;
  const invoice = db.getInvoiceById(invoiceId);
  if (!invoice) {
    return res.status(404).send('Not found');
  }
  // Authorize: ensure the invoice belongs to the authenticated user
  if (invoice.username !== username) {
    return res.status(403).send('Forbidden: invoice does not belong to authenticated user');
  }
  res.json(invoice);
});

3) Use parameterized queries or an ORM that enforces tenant scoping to prevent ID manipulation at the data layer. Always treat user-supplied identifiers as untrusted and validate against the authenticated identity.

middleBrick’s OpenAPI/Swagger analysis can help identify routes with path parameters that lack corresponding security or scope constraints. By cross-referencing spec definitions with runtime checks, it highlights endpoints where object references are not bound to the authenticated subject, supporting remediation aligned with compliance frameworks such as OWASP API Top 10 and SOC2.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Does Basic Auth alone prevent IDOR if passwords are strong?
No. Strong passwords prevent unauthorized authentication, but they do not enforce data ownership. IDOR is about authorization, not authentication; you must still scope data access to the authenticated user.
How can I test my fixes for IDOR in Express with Basic Auth?
Use a tool or scanner to make authenticated requests as one user to endpoints owned by another user (e.g., GET /api/users/2 when authenticated as user1). Confirm the server returns 403 or equivalent, and validate that data isolation is enforced per request.