Phishing Api Keys in Express with Api Keys
Phishing API Keys in Express with API Keys
In Express applications, storing and transmitting API keys as bearer tokens or custom headers can expose them to phishing when endpoints are misconfigured or when client-side code leaks secrets. A common pattern is to pass an API key via an Authorization header (e.g., Authorization: Bearer {key}) to a backend service that then forwards it to a third-party API. If the Express route does not validate the origin of the request and reflects the key in responses or errors, an attacker can phish the key by tricking a user or client into making authenticated requests that return the key in logs, error messages, or even JavaScript callbacks.
Consider an Express route that proxies requests to a payment provider and echoes back the provider’s response, including debug information. If the route attaches the upstream API key to the request and the response includes headers or body content that reveal the key, a phishing page can capture the key via a crafted request. For example, an attacker might host a page that sends repeated requests to the Express endpoint, attempting to harvest keys via social engineering or automated scraping. Because API keys are long-lived credentials compared to session tokens, leaked keys can be reused across services, leading to prolonged access and data exposure.
Another vector arises when API keys are embedded in frontend JavaScript or configuration files served by Express static assets. If these files are inadvertently exposed or if source maps are accessible, an attacker can extract keys directly. Phishing in this context often involves luring a developer or user to a malicious site that interacts with the Express API, causing the API key to be transmitted to an attacker-controlled server. This is especially risky when rate limiting or authentication is absent, allowing attackers to automate key discovery. The risk is compounded when the same key is used across multiple integrations, as a single leak can cascade across systems.
During a scan, middleBrick tests unauthenticated endpoints for key exposure by probing responses for sensitive patterns and analyzing spec-defined security schemes. For API key-based security schemes in an OpenAPI spec (type: apiKey), it checks whether keys are transmitted securely and whether responses or error traces could reflect them. This helps identify routes where keys might be phished through improper handling, even if the key itself is not stored in the database.
API Keys-Specific Remediation in Express
To mitigate phishing risks around API keys in Express, avoid reflecting keys in responses and ensure keys are never exposed to the client. Use environment variables to store keys, and proxy requests without echoing upstream secrets in logs or responses. Implement strict CORS policies and validate the origin of requests to prevent unauthorized cross-origin interactions that could be leveraged for phishing.
Below are concrete code examples demonstrating secure handling of API keys in Express.
| Pattern | Risk | Remediation |
|---|---|---|
| Storing keys in code or logs | Key leakage via source code or console | Use environment variables and suppress key logging |
| Passing keys through query parameters | Key leakage in URLs and browser history | Use headers and HTTPS only |
| Reflecting upstream headers in responses | Key exposure in client or error responses | Strip sensitive headers before sending responses |
// Secure Express proxy example: do not forward API key in response
const express = require('express');
const axios = require('axios');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// API key stored securely in environment
const API_KEY = process.env.THIRD_PARTY_API_KEY;
if (!API_KEY) {
console.error('Missing API key in environment');
process.exit(1);
}
app.get('/proxy-payment', async (req, res) => {
try {
// Do not include API key in response or logs
const upstreamResponse = await axios.get('https://api.payment.example.com/data', {
headers: {
Authorization: `Bearer ${API_KEY}`
},
timeout: 5000
});
// Only forward necessary data, never the key
res.json({
status: 'ok',
data: upstreamResponse.data
});
} catch (error) {
// Avoid exposing key or stack traces
console.error('Proxy request failed');
res.status(502).json({ error: 'Bad gateway' });
}
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// Secure static file serving: prevent key exposure via source maps or config
const express = require('express');
const path = require('path');
const app = express();
// Serve only necessary assets; exclude .env, .map files
app.use(express.static(path.join(__dirname, 'public'), {
dotfiles: 'deny',
setHeaders: (res, filePath) {
if (filePath.endsWith('.map') || filePath.includes('.env')) {
res.status(403).send('Forbidden');
}
}
}));
app.get('/config', (req, res) => {
// Never send API keys to the browser
res.json({ allowedEndpoints: ['/data', '/status'] });
});
app.listen(3000, () => console.log('App running'));
Use middleware to sanitize headers and enforce HTTPS in production-like environments. Validate that API keys are never included in error messages or response bodies. For continuous protection, integrate middleBrick’s scans via the CLI or GitHub Action to detect exposed keys and insecure proxy patterns before deployment.