Data Exposure with Api Keys
How Data Exposure Manifests in API Keys
API keys are secrets that grant programmatic access to services, and when they are unintentionally leaked they become a direct vector for abuse. Common exposure paths include:
- Hard‑coded keys in source code that is committed to public repositories.
- Keys printed to debug logs or console output during development or troubleshooting.
- Keys reflected in error messages or stack traces returned to the client (e.g., a 500 response that includes the authorization header).
- Keys transmitted in URLs as query parameters, which may be logged by proxies, browsers, or referrer headers.
- Keys embedded in client‑side JavaScript or mobile bundles that can be extracted by reverse‑engineering.
For example, a Node.js Express route that logs the full request header can accidentally write a key to a log file:
app.get('/data', (req, res) => {
console.log('Incoming headers:', req.headers); // <-- may log Authorization: ApiKey xxx
// …handler logic
});
If the log aggregation system is accessible to attackers or the repository is public, the key is exposed. Similar issues appear in error handling middleware that returns res.status(500).json({ error: err.message, headers: req.headers }), leaking the key in the response body.
Api Keys-Specific Detection
middleBrick’s Data Exposure check looks for API‑key material anywhere in the unauthenticated attack surface. It runs a set of regex patterns that match common key formats (e.g., AWS Access Key IDs, Stripe live keys, Google API keys, Azure storage account keys) against:
- HTTP response bodies (including JSON, XML, HTML).
- Response headers such as
Authorization,X-Api-Key, or custom headers. - Query strings and URL paths (to catch keys placed as
?key=…). - JavaScript bundles fetched from the endpoint (passive scanning of static assets).
- Error messages or stack traces that may be returned in 5xx responses.
When a match is found, the scanner flags it as a Data Exposure finding with severity based on the key’s privilege level (e.g., a key with write access to a production database is marked high). The finding includes the exact location (URL, header name, line number in a JS file) and a short snippet for context.
Real‑world incidents illustrate why this check matters. CVE‑2020-13943 exposed Docker Engine API credentials through daemon logs when users ran docker login with a misconfigured credential store. Similarly, CVE‑2021-22986 showed how an improperly secured F5 BIG‑IP iControl REST endpoint could leak authentication tokens via error responses. middleBrick’s automated checks would detect the leaked key material in the responses or logs associated with those endpoints.
Api Keys-Specific Remediation
The goal is to keep the secret out of any artifact that an attacker can observe. Below are language‑agnostic practices and concrete code snippets.
1. Store keys outside of source code
Use environment variables or a secret manager, and never commit the raw value.
// Node.js – using dotenv (development) or AWS Secrets Manager (production)
require('dotenv').config();
const apiKey = process.env.MY_API_KEY;
if (!apiKey) {
throw new Error('Missing MY_API_KEY environment variable');
}
// …use apiKey in requests
For AWS Lambda:
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
const secretsClient = new SecretsManagerClient({});
export const handler = async () => {
const data = await secretsClient.send(new GetSecretValueCommand({
SecretId: 'prod/my-api-key'
}));
const apiKey = JSON.parse(data.SecretString).key;
// …call downstream API
};
2. Avoid logging or returning the key
Strip the Authorization header from logs and never echo it in error responses.
// Express middleware to sanitize logs
app.use((req, res, next) => {
const safeHeaders = { ...req.headers };
delete safeHeaders.authorization;
delete safeHeaders['x-api-key'];
console.log('Request headers (sanitized):', safeHeaders);
next();
});
// Central error handler – do not include request headers
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Internal Server Error' });
});
3. Do not place keys in URLs
Prefer header‑based authentication; if a third‑party API forces query‑string keys, use a proxy that injects the header and removes the query parameter before forwarding.
// Simple Node proxy that moves a query‑string key to a header
app.get('/proxy/*', async (req, res) => {
const key = req.query.key;
if (!key) return res.status(400).send('Missing key');
const targetUrl = req.params[0];
const proxyReq = http.request(targetUrl, {
method: req.method,
headers: { ...req.headers, 'x-api-key': key }
}, proxyRes => {
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(res);
});
req.pipe(proxyReq);
});
4. Rotate and limit scope
Issue short‑lived keys or tokens, restrict them to the least privilege needed, and automate rotation via your secret manager’s lifecycle policies.
By applying these defenses, the Data Exposure check in middleBrick will no longer find API‑key material in the unauthenticated surface, and the overall security score will improve.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |