Api Key Exposure in Express
How Api Key Exposure Manifests in Express
Api Key Exposure in Express applications typically occurs through several Express-specific patterns. The most common scenario involves accidentally logging API keys during request handling. Express middleware like morgan or custom logging can capture request headers, query parameters, or request bodies where API keys are transmitted. For example, a naive logging middleware might log req.headers directly:
app.use((req, res, next) => {
console.log('Request headers:', req.headers);
next();
});
This logs Authorization headers containing API keys to stdout, which may be captured in log aggregation systems or monitoring tools.
Another Express-specific pattern is improper error handling. Express error middleware receives the error object and request context, and developers often log these without filtering sensitive data:
app.use((err, req, res, next) => {
console.error('Error:', err, req.query);
res.status(500).send('Internal Server Error');
});
If API keys are passed as query parameters, they appear in req.query and get logged on errors. Express route handlers that catch-all with app.all('*') can also inadvertently expose API keys by logging request objects without sanitization.
Environment variable exposure through Express configuration is another risk. When using dotenv or similar, developers might accidentally expose process.env in development error pages or health check endpoints:
app.get('/health', (req, res) => {
res.json({
status: 'ok',
env: process.env // Exposes API keys!
});
});
Third-party middleware can also be problematic. Many Express middleware packages log request data by default, and if they're not configured to exclude sensitive headers, API keys in Authorization headers get logged automatically.
Express-Specific Detection
Detecting API key exposure in Express requires examining both code patterns and runtime behavior. Static analysis should look for specific Express patterns:
Search for console.log, console.error, and logging middleware that directly logs req.headers, req.query, or req.body without filtering. Look for error middleware that logs the entire request object. Use ESLint rules like no-console and custom rules that flag direct logging of request objects.
Runtime detection with middleBrick involves scanning your Express API endpoints for API key exposure patterns. middleBrick tests unauthenticated endpoints by sending requests with various Authorization header formats (Bearer, Basic, API-Key) and analyzes responses for key leakage:
middlebrick scan https://your-express-app.com/api
middleBrick's black-box scanning sends test requests to your Express endpoints and analyzes the responses for exposed API keys. It checks for keys appearing in:
- Response headers (including CORS headers)
- Response bodies (JSON, XML, HTML)
- Error messages and stack traces
- Health check endpoints
- Documentation endpoints
The scanner also examines your OpenAPI spec if available, looking for API key definitions in the security schemes section and verifying they're properly implemented. For Express apps using express-openapi-validator or similar, middleBrick checks that API key validation doesn't inadvertently expose keys in error responses.
middleBrick's LLM/AI security checks are particularly relevant if your Express app uses AI features. The scanner tests for system prompt leakage and prompt injection vulnerabilities that could expose API keys stored in prompts or context.
Express-Specific Remediation
Remediating API key exposure in Express requires both code changes and configuration updates. Start with request logging middleware:
Replace naive logging with sanitized versions:
const express = require('express');
const app = express();
// Sanitized logging middleware
app.use((req, res, next) => {
const sanitizedHeaders = { ...req.headers };
delete sanitizedHeaders.authorization;
delete sanitizedHeaders['api-key'];
console.log('Request:', {
method: req.method,
url: req.url,
headers: sanitizedHeaders,
ip: req.ip
});
next();
});
For error handling, create middleware that filters sensitive data:
app.use((err, req, res, next) => {
const errorLog = {
message: err.message,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,
method: req.method,
url: req.url
};
console.error('Error:', errorLog);
res.status(500).send('Internal Server Error');
});
Use environment-specific logging levels. In production, log only essential information and never include request bodies or headers:
const logger = require('pino')({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug'
});
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production') {
logger.info({ method: req.method, url: req.url, ip: req.ip });
} else {
logger.debug(req.headers, req.query, req.body);
}
next();
});
For health check endpoints, explicitly exclude sensitive data:
app.get('/health', (req, res) => {
res.json({
status: 'ok',
uptime: process.uptime(),
timestamp: new Date().toISOString()
});
});
Configure third-party middleware to exclude sensitive headers. For morgan, use a custom token:
const omitSensitive = (tokens, req, res) => {
const headers = req.headers;
const sensitive = ['authorization', 'api-key', 'x-api-key'];
const sanitized = Object.fromEntries(
Object.entries(headers).filter(([key]) => !sensitive.includes(key.toLowerCase()))
);
return JSON.stringify(sanitized);
};
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
Integrate middleBrick into your Express development workflow using the CLI or GitHub Action to catch API key exposure before deployment:
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run middleBrick Scan
run: middlebrick scan https://staging.your-app.com
Frequently Asked Questions
How can I test if my Express API keys are being logged without running middleBrick?
console.log(req.headers) or error middleware that logs err and req together.