HIGH insecure direct object referenceexpressapi keys

Insecure Direct Object Reference in Express with Api Keys

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

Insecure Direct Object Reference (BOLA/IDOR) in an Express API occurs when an endpoint uses user-supplied identifiers (such as a resource ID or username) to locate a server-side object without verifying that the authenticated subject has permission to access that specific instance. When API keys are used for authentication but authorization is missing or incomplete, the key identifies the client but not the scoped access to individual resources, allowing attackers to iterate through predictable identifiers and access other users’ data.

Consider an Express route that retrieves a user profile by ID without checking ownership:

// Risk: IDOR with API key authentication only
app.get('/api/users/:id', (req, res) => {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) return res.status(401).send('Missing API key');
  // validateApiKey(apiKey) might check key existence and scope, but does not ensure key owner can only access their own user record
  db.getUserById(req.params.id).then(user => {
    if (!user) return res.status(404).send('Not found');
    res.json(user);
  }).catch(() => res.status(500).send('Error'));
});

Here, the API key proves the caller is a known client, but it does not enforce that the client can only access the user record associated with that key. An attacker who knows or guesses another numeric ID can request /api/users/123 and obtain data they should not see. This is a classic BOLA/IDOR: direct object reference because the parameter id directly maps to a database row, and insecure because authorization is missing.

Another common pattern is exposing references that should be indirect, such as filenames or internal pointers, where the key does not restrict traversal. For example:

// Risk: IDOR via predictable file references with API key
app.get('/api/reports/:reportName', (req, res) => {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) return res.status(401).send('Missing API key');
  // No check that the report belongs to the API key owner
  res.sendFile(`/data/reports/${req.params.reportName}`);
});

If reportName is user-controlled and not scoped to the key owner, an attacker can enumerate reports across other users. Because the scan category BOLA/IDOR runs in parallel with Authentication and checks for missing ownership validation, middleBrick would flag this as a high-severity finding with remediation guidance to enforce instance-level authorization.

In summary, combining Express routes with API key authentication creates risk when object references are not bound to the subject’s permissions. The key identifies the client, but without additional checks on the resource identifier, direct object references remain insecure.

Api Keys-Specific Remediation in Express — concrete code fixes

To fix BOLA/IDOR while keeping API keys as the authentication primitive, enforce that each operation validates ownership or scope relative to the key. Maintain a mapping between the API key and the allowed resource identifiers (e.g., tenant ID, user ID), and assert that the requested resource belongs to that scope.

Example remediation that ties the API key to a user ID and ensures users only access their own profile:

// Secure: enforce ownership based on API key mapping
const apiKeyToUserId = new Map(); // In practice, load from a secure store

app.get('/api/users/:id', (req, res) => {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) return res.status(401).send('Missing API key');

  const allowedUserId = apiKeyToUserId.get(apiKey);
  if (allowedUserId == null) return res.status(403).send('Invalid API key');

  if (req.params.id !== allowedUserId) {
    return res.status(403).send('Forbidden: you can only access your own record');
  }

  db.getUserById(req.params.id).then(user => {
    if (!user) return res.status(404).send('Not found');
    res.json(user);
  }).catch(() => res.status(500).send('Error'));
});

This ensures the API key maps to a subject, and the subject must match the object identifier in the request. For collections, scope the query to the subject instead of trusting the client-supplied identifier:

// Secure: scope database query to the subject derived from API key
app.get('/api/reports/:reportName', (req, res) => {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) return res.status(401).send('Missing API key');

  const ownerId = apiKeyToUserId.get(apiKey);
  if (ownerId == null) return res.status(403).send('Invalid API key');

  // Only fetch reports where owner_id matches the key owner
  db.getReportByNameAndOwner(req.params.reportName, ownerId).then(report => {
    if (!report) return res.status(404).send('Not found or access denied');
    res.json(report);
  }).catch(() => res.status(500).send('Error'));
});

Additional recommendations include rotating API keys, scoping keys to roles or tenants, and continuous monitoring of access patterns. middleBrick’s Pro plan supports continuous monitoring and can integrate into your CI/CD pipeline via the GitHub Action to fail builds if risk thresholds are exceeded; the MCP Server lets you run scans directly from your AI coding assistant within the IDE.

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

Can API keys alone prevent IDOR in Express?
No. API keys identify the client but do not enforce object-level permissions. You must map the key to a subject and validate that the subject is allowed to access the specific resource.
How does middleBrick detect IDOR in Express APIs?
middleBrick runs BOLA/IDOR checks in parallel with authentication and scans endpoints for missing ownership validation, direct object references, and unsafe consumption patterns, then provides prioritized findings with remediation guidance.