HIGH prototype pollutionexpressbearer tokens

Prototype Pollution in Express with Bearer Tokens

Prototype Pollution in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Prototype pollution in Express applications that accept Bearer tokens occurs when an attacker can modify the shared prototype of JavaScript objects through crafted input, and those inputs are later validated or deserialized in a context influenced by token-derived metadata. Bearer tokens themselves do not directly cause prototype pollution, but they often shape how request data is processed: route handlers commonly inspect Authorization headers, merge token claims with body or query parameters, and pass the combined object graph into vulnerable merge or assignment logic. When an Express app deserializes JSON payloads (for example via body-parser or custom middleware) and then mixes token claims—extracted from the Bearer token—into that payload, an attacker-supplied property such as __proto__, constructor.prototype, or a nested key can mutate Object.prototype or other constructor prototypes. This can lead to insecure behavior downstream, including privilege escalation when role claims derived from the token are modified, or when objects created from the polluted prototype are later used in access control decisions.

Consider an endpoint that accepts a JSON body and merges it with decoded token claims before processing. If the merge is shallow or uses a library that does not protect against prototype pollution, an attacker can send a payload like { "__proto__": { "isAdmin": true } } alongside a valid Bearer token. After merging, the resulting object may reflect the polluted prototype, causing role-based checks to incorrectly grant elevated privileges. In the OWASP API Top 10 context, this maps to API1:2023 – Broken Object Level Authorization and can be chained with BOLA/IDOR findings identified by middleBrick’s BOLA/IDOR and Property Authorization checks. Similarly, unchecked assignment into nested objects can pollute constructors used across the application, leading to unexpected state and potential injection outcomes when the object is later serialized or used in business logic.

Middleware that parses Bearer tokens and then forwards or transforms requests (for example in SSRF or outbound call scenarios) can also expose prototype pollution indirectly. If token claims are injected into objects that participate in URL construction or are stored in shared caches, polluted prototypes may affect serialization formats or validation routines that assume integrity of object shapes. middleBrick’s Input Validation and Property Authorization checks are designed to surface these risky mappings between token-derived data and mutable object shapes, while the LLM/AI Security checks can detect prompt-injection patterns that attempt to exploit compromised object prototypes in AI-assisted workflows.

Real-world exploit patterns include using constructor.prototype to add or modify properties on built-in objects, or leveraging libraries like lodash with unsafe deep merge configurations. A realistic Express snippet that is vulnerable might look like:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());

app.post('/merge', (req, res) => {
  const tokenClaims = { role: req.headers['authorization'] ? req.headers['authorization'].split(' ')[1] : '' }; // naive extraction
  const merged = { ...tokenClaims, ...req.body }; // vulnerable merge
  if (merged.role === 'admin') {
    res.send('admin');
  } else {
    res.send('user');
  }
});

app.listen(3000);

An attacker can send a Bearer token with any role claim and a body containing { "__proto__": { "role": "admin" } }, and depending on the merge implementation, the resulting merged object may reflect the polluted prototype, bypassing intended authorization. This demonstrates the importance of validating and sanitizing all incoming data, regardless of whether it originates from headers or the token payload, and of using robust merge strategies that avoid prototype modification.

Bearer Tokens-Specific Remediation in Express — concrete code fixes

Remediation focuses on preventing prototype pollution at the point where token-derived data and request payloads are combined. Use strict parsing and deep-copy libraries that are hardened against prototype pollution, validate and whitelist expected fields, and avoid merging objects directly using spread or Object.assign without safeguards. For Express, prefer explicit extraction of needed claims and schema-based validation rather than implicit merging.

1) Use a hardened merge that ignores dangerous keys and validates shape:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());

const safeMerge = (tokenClaims, body) => {
  const allowed = ['role', 'sub', 'scope'];
  const result = {};
  for (const key of allowed) {
    if (Object.prototype.hasOwnProperty.call(tokenClaims, key)) {
      result[key] = tokenClaims[key];
    }
    if (Object.prototype.hasOwnProperty.call(body, key)) {
      result[key] = body[key];
    }
  }
  return result;
};

app.post('/merge', (req, res) => {
  // naive bearer token extraction for example; use a JWT library in production
  const auth = req.headers['authorization'] || '';
  const tokenPart = auth.startsWith('Bearer ') ? auth.slice(7) : '';
  const tokenClaims = { role: tokenPart === 'admin' ? 'admin' : 'user' }; // simplified
  const merged = safeMerge(tokenClaims, req.body);
  if (merged.role === 'admin') {
    res.send('admin');
  } else {
    res.send('user');
  }
});

app.listen(3000);

2) Validate and parse Bearer tokens with a library (e.g., jsonwebtoken) and avoid putting raw token strings into objects that are merged; instead, extract only required claims and ensure body input conforms to a schema:

const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const app = express();
app.use(bodyParser.json());

const PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC_KEY-----';

app.post('/merge', (req, res) => {
  const auth = req.headers['authorization'] || '';
  const token = auth.startsWith('Bearer ') ? auth.slice(7) : null;
  let claims = {};
  if (token) {
    try {
      claims = jwt.verify(token, PUBLIC_KEY);
    } catch (err) {
      return res.status(401).send('invalid token');
    }
  }
  // schema validation for body, e.g., using an object with expected shape
  if (typeof req.body.role !== 'string' || !['admin', 'user'].includes(req.body.role)) {
    return res.status(400).send('invalid role');
  }
  const mergedRole = req.body.role || claims.role || 'user';
  if (mergedRole === 'admin') {
    res.send('admin');
  } else {
    res.send('user');
  }
});

app.listen(3000);

3) For production, combine schema validation (e.g., using a library like Joi or Zod) with environment-based secret management, and avoid prototype-unsafe utilities. The following example uses Zod-like style (pseudocode) to enforce strict shapes and prevent pollution:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());

const validateInput = (body) => {
  // In practice, use a schema validator to enforce exact shapes
  if (typeof body.role !== 'string' || !['admin', 'user'].includes(body.role)) {
    throw new Error('invalid role');
  }
  return body;
};

app.post('/merge', (req, res) => {
  const auth = req.headers['authorization'] || '';
  const token = auth.startsWith('Bearer ') ? auth.slice(7) : null;
  // Decode and verify token with a JWT library; here we simulate claims
  const tokenClaims = token === 'valid-token' ? { role: 'user' } : { role: 'guest' };
  try {
    const safeBody = validateInput(req.body);
    const mergedRole = safeBody.role || tokenClaims.role;
    res.send(mergedRole === 'admin' ? 'admin' : 'user');
  } catch (err) {
    res.status(400).send(err.message);
  }
});

app.listen(3000);

These examples emphasize explicit control over data flows involving Bearer tokens, avoiding implicit merges that can lead to prototype pollution and BOLA-related issues. By validating and whitelisting fields, you reduce the risk identified by middleBrick’s checks and align with secure coding practices for API authorization and input validation.

Frequently Asked Questions

Can middleware that extracts Bearer tokens inadvertently contribute to prototype pollution?
Yes, if the middleware merges token claims with request objects using unsafe patterns (e.g., spread or Object.assign) without guarding against __proto__ or constructor.prototype, it can introduce pollution. Use explicit field extraction and schema validation to avoid this.
How does middleBrick detect risks related to Bearer tokens and prototype pollution?
middleBrick runs 12 parallel security checks including Input Validation, Property Authorization, and LLM/AI Security. It analyzes OpenAPI specs and runtime behavior to surface findings like BOLA/IDOR and mappings to frameworks such as OWASP API Top 10 that can be affected by token-driven data flows.