HIGH excessive data exposureexpressbearer tokens

Excessive Data Exposure in Express with Bearer Tokens

Excessive Data Exposure in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Excessive Data Exposure occurs when an API returns more information than necessary in a response, and in Express applications using Bearer Tokens this often intersects with authentication and authorization logic. A typical pattern is an endpoint that accepts a Bearer Token in the Authorization header, uses it to identify a user, and then returns the full user record, including sensitive fields such as password hashes, reset tokens, internal identifiers, or role mappings. Because the token is included in every request, developers may assume the token alone is sufficient protection and inadvertently expose additional data beyond what the client needs.

In Express, routes commonly look up a user by decoding the token (e.g., using jsonwebtoken) and then sending back the user document directly, for example with res.json(user). If the user schema contains fields like password, passwordResetToken, emailVerificationToken, or internal IDs, those fields can be read even when the client only requested a profile summary. This becomes a chain: the token identifies identity, but identity does not imply the response should include every stored attribute. Attackers who intercept or obtain the token might also leverage authorization flaws such as Insecure Direct Object References (IDOR) to iterate over other user IDs and harvest excessive fields returned by endpoints that do not filter by role or scope.

Another scenario is when endpoints return server-side metadata or debugging information alongside intended data. For example, an Express route might include stack traces, verbose validation messages, or internal processing flags in production responses when errors are not properly sanitized. If a Bearer Token is used for routing or tenant isolation, missing checks can cause one tenant’s data to be returned along with system-level details, increasing the surface of exposure. MiddleBrick’s LLM/AI Security checks specifically look for System Prompt Leakage patterns and output scanning for PII or API keys; in Express contexts, this can highlight cases where token handling logic coincides with verbose or unfiltered output that should be trimmed or redacted.

Specification-driven analysis helps identify these risks by comparing the OpenAPI schema with runtime behavior. When an endpoint declares a minimal response schema but returns additional properties, the discrepancy can indicate exposure. For instance, an endpoint documented to return only { id, name, role } might in practice return { id, name, role, passwordHash, refreshToken, permissions, internalFlags } when a valid Bearer Token is supplied. Cross-referencing the spec definitions with actual responses allows detection of such mismatches. This is important because tokens often gate access to richer data sets, and without strict schema enforcement and response filtering, the presence of a valid token can unintentionally amplify data exposure.

Real-world attack patterns include enumeration via error messages and timing differences, where an attacker sends requests with valid but different Bearer Tokens to learn whether larger or more detailed responses correlate with higher privileges. OWASP API Top 10 categories such as Broken Object Level Authorization and Excessive Data Exposure intersect here, and compliance frameworks like PCI-DSS and SOC2 require minimization of data returned per request. In Express, mitigations include explicitly selecting fields to return, removing sensitive properties before serialization, and enforcing role- and scope-based filtering so that the presence of a Bearer Token does not imply broader data disclosure.

Bearer Tokens-Specific Remediation in Express — concrete code fixes

To remediate Excessive Data Exposure in Express while using Bearer Tokens, focus on strict response shaping and token-aware authorization checks. Below are concrete, working examples that demonstrate secure practices.

1. Explicitly select response fields

Instead of sending the entire Mongoose or plain object, return only necessary fields. This ensures that sensitive fields such as password hashes are never included.

// Secure route example
const express = require('express');
const jwt = require('jsonwebtoken');
const router = express.Router();

router.get('/profile', (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  const token = authHeader.split(' ')[1];
  let payload;
  try {
    payload = jwt.verify(token, process.env.JWT_SECRET);
  } catch (err) {
    return res.status(401).json({ error: 'Invalid token' });
  }
  // Assume getUserById fetches from DB
  getUserById(payload.sub).then(user => {
    if (!user) {
      return res.status(404).json({ error: 'Not found' });
    }
    // Explicitly shape the response
    const safeProfile = {
      id: user._id,
      name: user.name,
      email: user.email,
      role: user.role,
      scope: user.scope
    };
    res.json(safeProfile);
  }).catch(() => res.status(500).json({ error: 'Internal error' }));
});

module.exports = router;

2. Remove sensitive fields before serialization

If you work with objects that may contain sensitive keys, explicitly delete or omit them before sending the response.

// Remove sensitive fields
router.get('/users/:id', (req, res) => {
  const authHeader = req.headers.authorization;
  const token = authHeader && authHeader.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'Unauthorized' });

  const currentUser = verifyTokenAndGetUser(token); // custom helper
  getUser(req.params.id).then(user => {
    if (!user) return res.status(404).json({ error: 'Not found' });
    // Remove fields that should not be exposed
    delete user.passwordHash;
    delete user.refreshToken;
    delete user.emailVerificationToken;
    // Optionally redact based on role
    if (currentUser.role !== 'admin') {
      delete user.internalFlags;
      delete user.auditLogs;
    }
    res.json(user);
  }).catch(() => res.status(500).json({ error: 'Internal error' }));
});

3. Enforce scope- and role-based filtering

Use middleware to validate that the token grants access to the requested data shape and that the response does not exceed allowed scopes.

// Scope-based middleware
function ensureScope(requiredScope) {
  return (req, res, next) => {
    const authHeader = req.headers.authorization;
    const token = authHeader && authHeader.split(' ')[1];
    if (!token) return res.status(401).json({ error: 'Unauthorized' });
    const payload = verifyToken(token);
    if (!payload.scopes || !payload.scopes.includes(requiredScope)) {
      return res.status(403).json({ error: 'Insufficient scope' });
    }
    req.user = payload;
    next();
  };
}

router.get('/reports/:reportId', ensureScope('read:reports'), (req, res) => {
  getReport(req.params.reportId).then(report => {
    if (!report) return res.status(404).json({ error: 'Not found' });
    // Return a subset aligned with scope
    res.json({
      id: report._id,
      title: report.title,
      createdAt: report.createdAt
    });
  }).catch(() => res.status(500).json({ error: 'Internal error' }));
});

4. Centralize response handling to avoid accidental leakage

Use a wrapper to ensure every response goes through a safe serialization step that omits sensitive keys.

function safeResponse(data) {
  const omit = ['passwordHash', 'refreshToken', 'emailVerificationToken', 'internalFlags', 'auditLogs'];
  const clone = Array.isArray(data) ? data.map(item => {
    const obj = { ...item };
    omit.forEach(key => delete obj[key]);
    return obj;
  }) : { ...data };
  omit.forEach(key => delete clone[key]);
  return clone;
}

app.use((req, res, next) =>

5. Validate and sanitize inputs to prevent IDOR-assisted data exposure

Even with Bearer Tokens, ensure that users can only access resources they own or are explicitly permitted to view, reducing the chance that a valid token leads to excessive data exposure through IDOR.

// IDOR protection with ownership check
router.get('/users/:id', (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];
  const payload = token ? verifyToken(token) : null;
  if (!payload) return res.status(401).json({ error: 'Unauthorized' });

  const targetId = req.params.id;
  if (payload.sub !== targetId && payload.role !== 'admin') {
    return res.status(403).json({ error: 'Forbidden' });
  }

  getUser(targetId).then(user => {
    if (!user) return res.status(404).json({ error: 'Not found' });
    res.json({ id: user._id, name: user.name, email: user.email, role: user.role });
  }).catch(() => res.status(500).json({ error: 'Internal error' }));
});

These examples illustrate how to combine Bearer Token validation with disciplined response shaping and ownership checks to mitigate Excessive Data Exposure in Express APIs.

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

How does middleBrick detect Excessive Data Exposure in Express APIs using Bearer Tokens?
middleBrick compares the declared OpenAPI/Swagger response schema with actual runtime responses for endpoints that require Bearer Tokens. It flags mismatches where additional sensitive properties such as password hashes, tokens, or internal flags are returned beyond what is documented, and it highlights findings in the Data Exposure category with severity and remediation guidance.
Can using Bearer Tokens alone prevent Excessive Data Exposure in Express APIs?
No. Bearer Tokens primarily identify and authenticate requests; they do not limit what data an endpoint returns. Without explicit response filtering, schema enforcement, and role- or scope-based field selection, a valid token can still lead to Excessive Data Exposure. Defense requires disciplined serialization and authorization checks in Express code.