Api Key Exposure in Express with Jwt Tokens
Api Key Exposure in Express with Jwt Tokens — how this specific combination creates or exposes the vulnerability
In Express applications, storing API keys alongside JWTs on the server or in client-side storage can inadvertently expose both long-lived secrets and short-lived access tokens. An API key is typically a static credential used to identify a service or app, while a JWT carries claims and is often used for user authentication. When these are combined insecurely—such as embedding an API key inside a JWT payload or logging both together—the exposure of one can lead to compromise of the other.
For example, if an Express route stringifies sensitive configuration that includes an API key and includes it in a JWT without encryption, any leak of the JWT (through logs, error messages, or client-side storage) also exposes the API key. This is particularly risky when tokens are issued with broad scopes or long expiration times, increasing the window for misuse. Attackers can replay captured JWTs alongside exposed API keys to access backend services, call paid endpoints, or pivot within the infrastructure.
Middleware that fails to validate the origin of requests may inadvertently echo headers containing both Authorization tokens and custom identifiers that include API keys. Insufficient input validation can also allow injection through headers or query parameters, leading to information disclosure via error responses. For instance, malformed JWTs or missing key checks can cause the application to reveal stack traces or configuration details that include API keys.
Another vector arises when services exchange JWTs and API keys as part of internal communication without enforcing strict transport security or mutual verification. If an Express service trusts incoming JWTs without verifying signatures properly, an attacker who obtains a valid token—perhaps through insecure deserialization or weak key management—can use embedded or referenced API keys to call external APIs. This can result in unauthorized data access, excessive rate usage, or data exfiltration, all while appearing as legitimate authenticated traffic.
Patterns such as logging entire request headers, including authorization fields, or storing secrets in environment variables that are accidentally serialized into JWTs increase the likelihood of exposure. Without proper separation between authentication tokens and service credentials, an Express application can unintentionally create a pathway where a single leaked JWT leads to broader compromise. Regular rotation of keys, scoping tokens to least privilege, and strict header management are essential to reduce the risk surface when using JWTs in Express.
Jwt Tokens-Specific Remediation in Express — concrete code fixes
To reduce risk, ensure JWTs never carry API keys and that both are handled with strict controls. Use environment variables for API keys and access them securely without embedding them in tokens. Validate and verify JWTs using a robust library, enforce short expiration times, and apply consistent middleware to reject malformed or unsigned tokens.
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const API_KEY = process.env.API_KEY;
const JWT_SECRET = process.env.JWT_SECRET;
// Middleware to verify JWT and attach user info
function verifyJwt(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing or invalid authorization header' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
// Example route that uses API key server-side only
app.get('/external-data', verifyJwt, (req, res) => {
// API key is used server-to-server, never exposed to client or JWT
fetch('https://api.external.com/resource', {
headers: { 'X-API-Key': API_KEY }
})
.then(response => response.json())
.then(data => res.json(data))
.catch(() => res.status(502).json({ error: 'Upstream failure' }));
});
app.listen(3000, () => console.log('Server running on port 3000'));
Ensure logs exclude sensitive headers and never serialize API keys into JWT claims. Rotate secrets regularly and use asymmetric signing where feasible. For continuous protection in development and deployment, integrate the middleBrick dashboard to track risk trends, leverage the middlebrick CLI for local scans, or add the GitHub Action to fail builds when issues are detected. Teams using AI-assisted development can also install the MCP Server to scan APIs directly from their coding assistant.