Dangling Dns in Koa (Javascript)
Dangling DNS in Koa with JavaScript — how this specific combination creates or exposes the vulnerability
Dangling DNS occurs when a subdomain points to a service that no longer exists (e.g., a deleted cloud resource, expired SaaS tenant, or decommissioned third-party integration), but the DNS record remains active. In a Koa.js application, this vulnerability is exposed when the app makes outbound HTTP requests to external domains based on user-controlled input without proper validation. For example, if a Koa route uses ctx.request.query.url to fetch data from an external API and passes it directly to fetch or axios, an attacker can manipulate the parameter to target a dangling subdomain they control or that points to a vulnerable internal service.
Consider a Koa endpoint designed to preview URLs: it accepts a url query parameter, fetches the content, and returns metadata. If the application does not validate that the hostname is in an allowed list, an attacker could supply a URL like http://dangling-service.example.com/internal-admin. If dangling-service.example.com once pointed to a now-deleted AWS Elastic Load Balancer but the DNS record still exists, an attacker could re-register the domain (if expired) or exploit misconfigured cloud resources to host malicious content. The Koa app, unaware, would make the request and potentially expose internal networks via SSRF or leak sensitive data.
This risk is amplified in Koa because of its minimalist, middleware-driven design. Developers often implement custom fetch logic without centralized validation, relying on ad-hoc checks. Unlike frameworks with built-in URL validation (e.g., Express with helmet or specialized middleware), Koa requires explicit effort to sanitize outbound requests. The combination of Koa’s flexibility and missing outbound request controls makes dangling DNS exploitation a realistic path to SSRF, cloud metadata access, or internal service enumeration when paired with user-influenced outbound traffic.
JavaScript-Specific Remediation in Koa — concrete code fixes
To mitigate dangling DNS risks in Koa, implement strict outbound request validation using an allowlist of permitted hostnames. This must be done at the middleware level before any outbound HTTP request is made. Below is a syntactically correct Koa middleware that validates the hostname of a user-provided URL against a predefined list of allowed domains.
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// Define allowed outbound destinations
const ALLOWED_HOSTS = new Set([
'api.example.com',
'data.trustedpartner.org',
'graphs.metrics.net'
]);
// Middleware to validate outbound URL hostname
function validateOutboundURL() {
return async (ctx, next) => {
const { url } = ctx.query;
if (!url) {
ctx.status = 400;
ctx.body = { error: 'Missing url parameter' };
return;
}
let parsed;
try {
parsed = new URL(url);
} catch (err) {
ctx.status = 400;
ctx.body = { error: 'Invalid URL format' };
return;
}
const hostname = parsed.hostname.toLowerCase();
// Optional: block IP addresses to prevent DNS bypass
if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname) ||
/^\[[0-9a-f:]+\]$/.test(hostname)) {
ctx.status = 400;
ctx.body = { error: 'IP addresses are not allowed' };
return;
}
if (!ALLOWED_HOSTS.has(hostname)) {
ctx.status = 403;
ctx.body = { error: 'Hostname not allowed' };
return;
}
// Attach parsed URL to state for use in route handler
ctx.state.parsedURL = parsed;
await next();
};
}
// Route that uses validated URL
router.get('/fetch-metadata', validateOutboundURL(), async (ctx) => {
const { parsedURL } = ctx.state;
try {
const response = await fetch(parsedURL.toString(), {
timeout: 5000,
redirect: 'manual' // Prevent automatic redirect attacks
});
if (!response.ok) {
ctx.status = 502;
ctx.body = { error: 'Upstream request failed' };
return;
}
const data = await response.text();
ctx.body = { metadata: { length: data.length, contentType: response.headers.get('content-type') } };
} catch (err) {
ctx.status = 500;
ctx.body = { error: 'Request failed', details: err.message };
}
});
app.use(router.routes());
app.listen(3000);
This approach ensures that even if a dangling DNS record points to an attacker-controlled domain, the request is blocked unless the hostname is explicitly allowed. Combine this with network-level controls (egress filtering) and regular DNS inventory audits to reduce exposure. middleBrick can help detect such misconfigurations by scanning for SSRF vectors and identifying outbound requests to unauthorized domains during its black-box testing of the unauthenticated attack surface.