Dangling Dns with Api Keys
How Dangling Dns Manifests in Api Keys
A dangling DNS record occurs when a domain’s CNAME or ALIAS points to a hostname that no longer exists (e.g., a deleted cloud service, a removed SaaS tenant, or a de‑provisioned load balancer). If an API client hard‑codes or dynamically builds a request URL using that subdomain, the request will be resolved to an IP address that an attacker can later claim. When the attacker controls the resulting endpoint, any API key sent in the request header, query string, or body is exfiltrated.
Typical code paths where this appears include:
- Configuration‑driven service discovery:
const apiUrl = process.env.SERVICE_SUBDOMAIN + '.example.com';where the environment variable is populated from a config map that references a CNAME. - Multi‑tenant SaaS platforms that generate tenant‑specific subdomains (e.g.,
tenant123.api.service.com) and later delete the tenant without cleaning the DNS zone. - CI/CD pipelines that inject a preview environment URL into API calls, where the preview environment is torn down but the DNS record remains.
- Third‑party SDKs that construct endpoint URLs from a base domain and a resource identifier, relying on the provider’s DNS to stay valid.
When the dangling subdomain is re‑registered by an attacker, they can host a malicious service that mimics the legitimate API. The client, unaware of the change, continues to transmit its API key (often a long‑lived secret) in the Authorization: Bearer header or as a query parameter ?api_key=.... The attacker then harvests the key and can impersonate the client, leading to unauthorized data access, privilege escalation, or cost abuse—especially dangerous for keys that unlock paid services or LLM back‑ends.
Api Keys-Specific Detection
Detecting dangling DNS in the context of API key usage requires observing both the DNS resolution of the request host and the presence of API key material in the traffic. middleBrick’s unauthenticated black‑box scan includes an Inventory Management check that enumerates discovered hostnames and attempts to resolve their CNAME/ALIAS chains. If a target hostname resolves via a CNAME to a domain that does not respond with a valid service (e.g., returns NXDOMAIN or a generic error page), the scanner flags a potential subdomain takeover.
During the same scan, middleBrick inspects outgoing requests for common API key patterns (e.g., Authorization: Bearer [A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+ for JWTs, x-api-key: headers, or query parameters named api_key, token, access_token). When a request containing such a credential is sent to a host flagged as having a dangling DNS record, the finding is correlated and reported as a high‑risk data‑exposure issue.
Example of a middleBrick CLI command that would surface this issue:
middlebrick scan https://api.example.com --output json
The resulting JSON includes a findings array where an entry might look like:
{
"id": "MS-INV-0042",
"name": "Potential subdomain takeover via dangling CNAME",
"severity": "high",
"category": "Inventory Management",
"description": "Hostname api‑preview.example.com resolves to CNAME example‑preview.azurewebsites.net, which does not host a valid service.",
"remediation": "Remove the CNAME record or point it to an active resource. Ensure API keys are not sent to unverified hosts.",
"evidence": {
"request": {
"method": "GET",
"url": "https://api-preview.example.com/v1/data",
"headers": {
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
}
}
This tells the developer exactly where the API key is being leaked and why the host is untrusted.
Api Keys-Specific Remediation
Remediation focuses on two fronts: eliminating the dangling DNS condition and ensuring API keys are never transmitted to unverified endpoints.
Fixing the DNS issue
- Implement a DNS ownership verification step in your deployment pipeline. Before creating a CNAME, confirm the target domain exists and is under your control.
- Use automated tools (e.g.,
dnsx,subfinder) to scan your zone for dangling CNAMEs on a regular schedule and delete or repoint them. - Prefer using A/AAAA records with IP addresses you control, or use a service‑mesh sidecar that performs mutual TLS authentication, which eliminates reliance on DNS for trust.
Protecting API key transmission
Even with clean DNS, defend against accidental key leakage:
- Store API keys in a secret manager (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager) and retrieve them at runtime, never hard‑coding them.
- Restrict the key’s usage scope: bind it to specific IP ranges, referrer headers, or short‑lived JWTs with limited expiration.
- Enforce HTTPS everywhere and enable HTTP Strict Transport Security (HSTS) to prevent downgrade attacks.
- Use request‑level allowlists: before sending a request, verify that the hostname matches a pre‑approved list (e.g., via an
allowlistarray in code). - Rotate keys regularly and automate revocation when a deployment is decommissioned.
Concrete code example in Node.js that combines these practices:
const https = require('https');
const { secretsManager } = require('@aws-sdk/client-secrets-manager');
const ALLOWED_HOSTS = ['api.example.com', 'api-staging.example.com'];
async function getApiKey() {
const client = new secretsManager.SecretsManagerClient({});
const resp = await client.send(new secretsManager.GetSecretValueCommand({
SecretId: 'prod/api-key'
}));
return JSON.parse(resp.SecretString).key;
}
function isHostAllowed(url) {
try {
const { hostname } = new URL(url);
return ALLOWED_HOSTS.includes(hostname);
} catch (_) {
return false;
}
}
async function callApi(endpoint) {
if (!isHostAllowed(endpoint)) {
throw new Error(`Endpoint hostname not allowed: ${endpoint}`);
}
const key = await getApiKey();
const options = {
method: 'GET',
hostname: new URL(endpoint).hostname,
path: new URL(endpoint).pathname + new URL(endpoint).search,
headers: {
'Authorization': `Bearer ${key}`,
'User-Agent': 'my-service/1.0'
}
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => resolve(JSON.parse(data)));
});
req.on('error', reject);
req.end();
});
}
// Usage
callApi('https://api.example.com/v1/resource').then(console.log).catch(console.error);
This snippet ensures the API key is fetched securely at runtime, the request host is checked against an allowlist, and the key is only sent over HTTPS. If a dangling DNS record were to point api.example.com to an attacker‑controlled host, the request would still be blocked because the hostname would not match the allowlist (assuming the attacker cannot register an exact match). Combined with regular DNS hygiene, this eliminates the data‑exposure path.