HIGH xss cross site scriptingexpressapi keys

Xss Cross Site Scripting in Express with Api Keys

Xss Cross Site Scripting in Express with Api Keys — how this specific combination creates or exposes the vulnerability

Cross-site scripting (XSS) in an Express API that exposes API keys often arises when error messages or debug output inadvertently reflect attacker-controlled input into HTML, JSONP, or JavaScript responses served to browsers. A common pattern is an endpoint that accepts an api_key query or header, uses it to look up data or configuration, and includes the key or related values directly in a response that the client evaluates as script. For example, if an Express route embeds the api_key into a JSON response consumed by a browser page without proper escaping, an attacker may try to inject script via the key value itself (e.g., a key they control) and observe whether the reflected payload executes in another user’s browser.

Another scenario involves an Express route that returns JavaScript to the client (e.g., a JSONP callback or a dynamically generated script) and incorporates the api_key or data derived from it into the script body without sanitization. If the api_key contains characters that break the script context (such as quotes or semicolons) and are reflected without encoding, an attacker can close the current script context and execute arbitrary code. This becomes especially risky when debug or verbose error responses reveal the presence of api_key values alongside stack traces or variable contents, effectively leaking secrets and providing an injection vector.

OWASP API Top 10 highlights injection and broken object level authorization as high risks; when api_key handling intersects with response content that reaches the browser, the API surface expands to include stored or reflected XSS. Input validation that only focuses on the route parameters but ignores how the api_key and associated data are rendered into JavaScript contexts can leave injection flaws open. Attack patterns such as script injection via reflected values, unsafe deserialization of tainted data, or misuse of templating engines that treat api_key strings as executable code are typical root causes in Express-based services.

Api Keys-Specific Remediation in Express — concrete code fixes

Remediation centers on never reflecting raw api_key values into browser-interpretable contexts, strict input validation, and safe handling of sensitive data in responses. When an api_key is required for authorization, keep it in server-side headers and never embed it in HTML, JSON, or JavaScript sent to the client. Use middleware to validate the key format and reject suspicious input before it reaches route handlers. Ensure responses are content-type appropriate and data is encoded for the target context (HTML, attribute, JavaScript, URL).

Express route examples

  • Safe JSON response without exposing api_key:
const express = require('express');
const app = express();

app.get('/data', (req, res) => {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey || !/^[A-Za-z0-9\-_=]+\.[A-Za-z0-9\-_=]+\.?[A-Za-z0-9\-_=]*$/.test(apiKey)) {
    return res.status(400).json({ error: 'Invalid request' });
  }
  // Verify apiKey server-side, e.g., against a store
  // Do NOT include apiKey in the response body
  res.json({ message: 'Success', data: { /* non-sensitive payload */ } });
});
app.listen(3000);
  • Safe handling with explicit content-type and escaping for HTML context:
const escapeHtml = (unsafe) => {
  if (typeof unsafe !== 'string') return unsafe;
  return unsafe.replace(/[<>"'&]/g, (c) => ({
    '&': '&',
    '<': '<',
    '>': '>',
    '"': '"',
    "'": '''
  }[c]));
};

app.get('/greet', (req, res) => {
  const name = req.query.name;
  const apiKey = req.headers['x-api-key'];
  if (!name || typeof name !== 'string') {
    return res.status(400).json({ error: 'Invalid name' });
  }
  // Never reflect apiKey; escape user-controlled data for HTML
  const safeName = escapeHtml(name);
  res.type('text/html').send(`<div>Hello, ${safeName}</div>`);
});
app.listen(3000);
  • Avoid JSONP with reflected api_key; if you must support JSONP, enforce strict callback naming and encode the payload:
const safeCallbackName = (str) => /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(str) ? str : 'callback';

app.get('/report', (req, res) => {
  const apiKey = req.headers['x-api-key'];
  const callback = safeCallbackName(req.query.callback);
  const payload = { status: 'ok', /* non-sensitive data only */ };
  res.type('application/javascript').send(`${callback}(${JSON.stringify(payload)});`);
});
app.listen(3000);
  • Input validation and sanitization libraries help enforce safe formats:
const validator = require('validator');

app.post('/submit', (req, res) => {
  const apiKey = req.headers['x-api-key'];
  const userInput = req.body.comment;
  if (!apiKey || !validator.isAlphanumeric(apiKey.replace(/[-_.]/g, ''), 'utf-8')) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  if (typeof userInput !== 'string' || !validator.escape(userInput).trim()) {
    return res.status(400).json({ error: 'Invalid comment' });
  }
  // Process safely; do not echo raw userInput or apiKey back to the browser
  res.json({ ok: true });
});
app.listen(3000);

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Does exposing API keys in error responses increase XSS risk?
Yes. If error messages or debug output reflect an api_key into HTML/JavaScript sent to the browser, attackers can combine the leaked secret with script injection to hijack sessions or execute code. Keep api keys out of client-side content and treat them as sensitive data.
Is JSONP safe to use with API keys in Express?
JSONP is generally unsafe because it executes arbitrary JavaScript. If you must use JSONP, strictly validate and encode the callback name and ensure the payload never includes raw api_key values. Prefer CORS and standard JSON instead.