HIGH broken access controlexpressbasic auth

Broken Access Control in Express with Basic Auth

Broken Access Control in Express with Basic Auth — how this specific combination creates or exposes the vulnerability

Broken Access Control occurs when authorization checks are missing, incomplete, or bypassed, allowing authenticated users to access or modify resources that should be restricted. In Express, combining HTTP Basic Authentication with weak or missing authorization logic amplifies this risk because Basic Auth only provides authentication (verifying who you are), not authorization (verifying what you are allowed to do).

When Basic Auth is used without role- or scope-based checks, any authenticated user can potentially reach endpoints that should be limited to administrators or specific user groups. For example, an authenticated regular user might send a GET request to an admin-only route like /api/admin/users and receive data if the server does not validate permissions after the credentials are verified. This misalignment between authentication and authorization is a common root cause of Insecure Direct Object References (IDOR) and Privilege Escalation, which are among the OWASP API Security Top 10 findings that middleBrick checks for across its 12 security categories.

Because Basic Auth credentials are sent in the Authorization header as a base64-encoded string (easily decoded), attackers who obtain or intercept credentials gain a valid identity that the server may trust broadly. Without middleware that enforces per-request authorization—such as verifying user roles, scopes, or ownership before returning or modifying data—an attacker can leverage a low-privilege account to perform high-privilege actions. This is especially dangerous when endpoints do not validate input identifiers against the authenticated user’s context, enabling IDOR-style access to other users’ resources.

middleBrick scans unauthenticated attack surfaces and flags these authorization gaps under BOLA/IDOR and BFLA/Privilege Escalation checks, providing findings with severity levels and remediation guidance rather than attempting to fix the API itself. The scanner also cross-references OpenAPI/Swagger definitions with runtime behavior to detect mismatches between documented access controls and actual endpoint behavior.

Basic Auth-Specific Remediation in Express — concrete code fixes

To secure Express APIs using Basic Auth, move from simple authentication to explicit authorization checks on every relevant route. Always decode and validate credentials, then enforce role- or scope-based access before performing any data operation. Below are concrete examples that demonstrate both vulnerable and remediated patterns.

Vulnerable Example: Authentication Without Authorization

const express = require('express');
const app = express();
const users = [
  { id: 1, username: 'alice', password: 'pass123', role: 'user' },
  { id: 2, username: 'admin', password: 'secret', role: 'admin' }
];

function basicAuth(req, res, next) {
  const header = req.headers.authorization;
  if (!header || !header.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }
  const token = header.split(' ')[1];
  const decoded = Buffer.from(token, 'base64').toString('utf-8');
  const [username, password] = decoded.split(':');
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) {
    return res.status(401).send('Invalid credentials');
  }
  req.user = user;
  next();
}

app.use(basicAuth);

app.get('/api/profile/:userId', (req, res) => {
  // Vulnerable: no check that req.user.id matches :userId
  const targetUser = users.find(u => u.id === Number(req.params.userId));
  if (!targetUser) return res.status(404).send('Not found');
  res.json({ id: targetUser.id, username: targetUser.username });
});

app.listen(3000);

This code authenticates the user but does not enforce that the authenticated user can only access their own profile. An attacker who knows another user’s ID can read or manipulate data across accounts.

Remediated Example: Enforcing Ownership and Roles

const express = require('express');
const app = express();
const users = [
  { id: 1, username: 'alice', password: 'pass123', role: 'user' },
  { id: 2, username: 'admin', password: 'secret', role: 'admin' }
];

function basicAuth(req, res, next) {
  const header = req.headers.authorization;
  if (!header || !header.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }
  const token = header.split(' ')[1];
  const decoded = Buffer.from(token, 'base64').toString('utf-8');
  const [username, password] = decoded.split(':');
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) {
    return res.status(401).send('Invalid credentials');
  }
  req.user = user;
  next();
}

function authorize(allowedRoles) {
  return (req, res, next) => {
    if (!allowedRoles.includes(req.user.role)) {
      return res.status(403).send('Forbidden');
    }
    next();
  };
}

app.use(basicAuth);

app.get('/api/profile/:userId', (req, res) => {
  // Enforce ownership: only allow access to own profile
  if (req.user.id !== Number(req.params.userId)) {
    return res.status(403).send('Forbidden: cannot access other users profiles');
  }
  const targetUser = users.find(u => u.id === Number(req.params.userId));
  if (!targetUser) return res.status(404).send('Not found');
  res.json({ id: targetUser.id, username: targetUser.username, role: targetUser.role });
});

app.get('/api/admin/users', authorize(['admin']), (req, res) => {
  // Only admins can reach this endpoint
  res.json(users.map(u => ({ id: u.id, username: u.username, role: u.role })));
});

app.listen(3000);

The remediated version introduces an authorize helper that checks roles and enforces ownership on the profile route. This aligns authentication with authorization, reducing the risk of IDOR and privilege escalation. middleBrick’s checks for BOLA/IDOR and BFLA/Privilege Escalation can validate that such controls are present and correctly applied.

Additional recommendations include avoiding hard-coded credentials in production, using HTTPS to protect the Basic Auth header in transit, and combining Basic Auth with other mechanisms (such as scopes or tokens) where appropriate. The GitHub Action can be used to add API security checks to your CI/CD pipeline, failing builds if risk scores exceed your defined thresholds, while the CLI allows scanning from the terminal with commands like middlebrick scan <url>.

Frequently Asked Questions

Does Basic Auth alone provide sufficient protection for sensitive endpoints?
No. Basic Auth only handles authentication, not authorization. Without explicit access controls per endpoint, authenticated users may access or modify data they should not. Combine Basic Auth with role- and ownership-based checks to enforce proper authorization.
Can middleware enforce ownership checks for resource IDs in Express with Basic Auth?
Yes. After verifying Basic Auth credentials, compare the authenticated user’s ID with the resource identifier in the request (e.g., URL parameter or body) before proceeding. Return 403 if they do not match, and use role checks for admin-only routes.