HIGH jwt misconfigurationapi keys

Jwt Misconfiguration with Api Keys

How JWT Misconfiguration Manifests in API Keys

When an API treats the same value used as an API key also as the secret for signing or verifying JSON Web Tokens, the two mechanisms become coupled. An attacker who can obtain or guess the API key can then forge valid JWTs, bypassing any intended token‑based authentication or authorization.

This coupling often appears in code that:

  • Extracts the API key from a request header (e.g., x-api-key) and directly passes it to jwt.verify() or jwt.sign() as the secret.
  • Fails to enforce the signing algorithm, allowing a token with the none algorithm to be accepted when the secret is known.
  • Uses a low‑entropy API key (e.g., a simple string like test123) as the JWT secret, making brute‑force feasible.

The vulnerable flow typically looks like this:

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();

// Insecure: API key reused as JWT secret
app.use((req, res, next) => {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) return res.status(401).send('Missing API key');
  req.apiKey = apiKey; // later used as JWT secret
  next();
});

app.get('/data', (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];
  try {
    // No algorithm enforcement; secret is the API key
    const payload = jwt.verify(token, req.apiKey);
    res.json({ message: 'Data', user: payload });
  } catch (err) {
    res.status(401).send('Invalid token');
  }
});

app.listen(3000);

If an attacker learns the API key (e.g., from logs, client‑side code, or a misconfigured public repo), they can create a token such as:

const jwt = require('jsonwebtoken');
const apiKey = 'leaked‑api‑key'; // obtained elsewhere
const forged = jwt.sign({ sub: 'admin', role: 'admin' }, apiKey, { algorithm: 'HS256' });
console.log(forged); // send this as Authorization header

The resulting token will be accepted by the vulnerable endpoint, granting the attacker privileged access.

API Keys‑Specific Detection

middleBrick’s unauthenticated black‑box scan includes checks that specifically target the coupling of API keys and JWT secrets. When you submit a URL, the scanner:

  • Identifies endpoints that require an x-api-key (or similar) header.
  • Attempts to replay a request with a valid API key to confirm the header is checked.
  • For each identified endpoint, tries to forge a JWT using the discovered API key as the HMAC secret.
  • Tests algorithm confusion by sending a token with the none algorithm and the API key as the secret.
  • Reports a finding when a forged token is accepted, indicating JWT misconfiguration tied to the API key.

You can run this check locally with the middleBrick CLI:

# Install the CLI (npm)
npm i -g middlebrick

# Scan an API endpoint
middlebrick scan https://api.example.com

The output will include a finding similar to:

Finding: JWT secret derived from API key
Severity: High
Category: Broken Authentication
Description: The API accepts JWTs signed with the x‑api‑key value as the HMAC secret, allowing token forgery.
Remediation: Use a separate, high‑entropy secret for JWT signing and verify the algorithm explicitly.

In the Dashboard, the finding appears under the "Authentication" category with a severity badge and a link to the exact request/response that demonstrated the issue.

API Keys‑Specific Remediation

The fix is to decouple API key authentication from JWT verification. Use the API key solely for identifying the caller, and rely on an independent, strong secret (or asymmetric key pair) for JWTs. Additionally, always enforce the signing algorithm.

Here is a corrected Node.js/Express example:

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();

// Independent secrets – load from environment, never hard‑code
const API_KEY = process.env.API_KEY;          // random, high‑entropy value
const JWT_SECRET = process.env.JWT_SECRET;    // separate secret for HS256
// For asymmetric keys you could use a private/public PEM pair

// Middleware: verify API key only
function verifyApiKey(req, res, next) {
  const key = req.headers['x-api-key'];
  if (key === API_KEY) {
    return next();
  }
  return res.status(401).send('Invalid API key');
}

// Middleware: verify JWT with explicit algorithm
function verifyJwt(req, res, next) {
  const auth = req.headers.authorization;
  if (!auth) return res.status(401).send('Missing token');
  const token = auth.split(' ')[1];
  try {
    const payload = jwt.verify(token, JWT_SECRET, { algorithms: ['HS256'] });
    req.user = payload;
    next();
  } catch (err) {
    return res.status(401).send('Invalid token');
  }
}

app.get('/data', verifyApiKey, verifyJwt, (req, res) => {
  res.json({ message: 'Secure data', user: req.user });
});

app.listen(3000);

Key points in the fix:

  • API_KEY and JWT_SECRET are distinct values stored securely (e.g., in a vault or environment variables).
  • The JWT verification explicitly limits acceptable algorithms to HS256 (or RS256 for asymmetric keys).
  • If you prefer asymmetric cryptography, replace the HMAC secret with a private key for signing and a public key for verification:

    const privateKey = fs.readFileSync('private.pem');
    const publicKey = fs.readFileSync('public.pem');
    // signing
    jwt.sign(payload, privateKey, { algorithm: 'RS256' });
    // verification
    jwt.verify(token, publicKey, { algorithms: ['RS256'] });
    
  • Rotate both the API key and JWT secret periodically, and ensure they are never logged or exposed in client‑side bundles.

After applying these changes, rescan the endpoint with middleBrick. The scanner will no longer accept a forged token, and the finding will disappear from the report.

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

Can I reuse the same value as both an API key and a JWT secret if I rotate it frequently?
No. Reusing the same value couples two independent security mechanisms. Even with rotation, an attacker who obtains the current value can forge tokens until the next rotation. The proper approach is to keep the API key and JWT secret separate, each with its own strong, randomly generated value.
Does middleBrick only test for HMAC‑based JWT weaknesses, or does it also check algorithm confusion like the "none" attack?
middleBrick performs both. It attempts to forge a token using the discovered API key as an HMAC secret and also sends a token with the "none" algorithm and the API key as the secret. If either is accepted, the scanner reports a JWT misconfiguration finding tied to the API key.