HIGH dangling dnshapimutual tls

Dangling Dns in Hapi with Mutual Tls

Dangling Dns in Hapi with Mutual Tls — how this specific combination creates or exposes the vulnerability

A Dangling DNS configuration in a Hapi server combined with Mutual TLS (mTLS) can expose the unauthenticated attack surface during the 5–15 second scan window. In Hapi, the server may resolve a hostname (e.g., internal service or metadata service) at route or plugin initialization time, or lazily on request. If TLS client authentication is enforced but the DNS resolution occurs before mTLS peer verification completes, an attacker who can influence the hostname (via path, query, or header) may cause the server to connect to an unintended internal endpoint. Because mTLS verifies the client certificate, the server trusts the client’s presented identity, but it does not inherently validate that the hostname it resolves aligns with the intended service. When the resolved DNS name points to an internal or external service not expected by the API, data may be sent to an unauthorized host, or sensitive responses may be returned to the client depending on how the server handles the upstream result.

During a black-box scan, middleBrick tests unauthenticated endpoints and checks whether user-controlled input can lead to off-host requests or data exposure. If a Hapi route uses a hostname from user input (e.g., a webhook URL or service discovery value) and performs DNS resolution at request time with mTLS enabled, the scan can detect whether the resolved target is internal or whether responses from unintended hosts are returned. This is especially relevant when the server does not validate the hostname against the certificate’s subject or SAN, allowing a mismatch that results in data being routed to an external or internal unintended peer. The combination therefore creates a risk where enforced mTLS gives a false sense of security while DNS resolution diverts traffic to unintended endpoints, potentially leaking data or enabling SSRF-like behaviors within the mTLS trust boundary.

For example, if a route accepts a hostname header and uses Node’s DNS lookup without additional validation, the server may resolve an internal service name that is not part of the expected service mesh. With mTLS enabled, the client certificate is verified, but the server does not ensure the resolved hostname matches the certificate’s intended target. middleBrick’s checks include testing whether user-controlled inputs can change network destinations and whether responses from unintended hosts are returned, which helps surface this class of configuration issue.

Mutual Tls-Specific Remediation in Hapi — concrete code fixes

Remediation focuses on ensuring DNS resolution aligns with mTLS expectations and that hostnames are validated against the peer certificate. Do not derive target hostnames from untrusted input. If you must resolve dynamic services, validate the resolved address against an allowlist and ensure the hostname used for TLS matches the certificate’s subject or SAN. Below are concrete Hapi examples that demonstrate safe patterns.

Example 1: Strict mTLS server with static target

Configure the server to use a fixed hostname for upstream calls and enforce client certificate verification without relying on dynamic DNS-derived targets.

const Hapi = require('@hapi/hapi');
const tls = require('tls');

const init = async () => {
  const server = Hapi.server({
    port: 443,
    tls: {
      key: fs.readFileSync('service.key'),
      cert: fs.readFileSync('service.crt'),
      ca: fs.readFileSync('ca.crt'),
      requestCert: true,
      rejectUnauthorized: true,
    },
  });

  server.route({
    method: 'POST',
    path: '/report',
    handler: (request, h) => {
      // Use a statically defined, validated endpoint
      const target = 'https://internal-reporter.example.com/ingest';
      // Perform request with mTLS using fixed hostname
      return tls.request({
        hostname: 'internal-reporter.example.com',
        port: 443,
        method: 'POST',
        cert: fs.readFileSync('service.key'),
        key: fs.readFileSync('service.key'),
        ca: fs.readFileSync('ca.crt'),
        servername: 'internal-reporter.example.com',
      }, (res) => {
        // handle response
      }).on('error', (err) => {
        request.logger.error(err);
      });
    },
  });

  await server.start();
};

Example 2: Validating dynamic hostnames against an allowlist

If you must resolve a hostname, resolve it, then confirm it belongs to an allowed set before using it in TLS options. Do not pass the raw user-supplied value to DNS or TLS configuration.

const Hapi = require('@hapi/hapi');
const dns = require('dns').promises;

const ALLOWED_HOSTS = new Set([
  'internal-reporter.example.com',
  'metrics.internal.example.com',
]);

const init = async () => {
  const server = Hapi.server({
    port: 443,
    tls: {
      key: fs.readFileSync('service.key'),
      cert: fs.readFileSync('service.crt'),
      ca: fs.readFileSync('ca.crt'),
      requestCert: true,
      rejectUnauthorized: true,
    },
  });

  server.route({
    method: 'POST',
    path: '/report/{host}',
    async handler(request, h) {
      const host = request.params.host;
      if (!ALLOWED_HOSTS.has(host)) {
        return h.response({ error: 'forbidden host' }).code(403);
      }
      // Verify DNS resolution matches expected host
      const resolved = await dns.resolve4(host);
      if (resolved.length === 0) {
        return h.response({ error: 'cannot resolve' }).code(400);
      }
      // Use servername matching the validated hostname
      const tlsOptions = {
        hostname: host,
        port: 443,
        method: 'POST',
        cert: fs.readFileSync('service.key'),
        key: fs.readFileSync('service.key'),
        ca: fs.readFileSync('ca.crt'),
        servername: host,
      };
      // Perform request with validated hostname
      return new Promise((resolve, reject) => {
        const req = tls.request(tlsOptions, (res) => {
          resolve({ statusCode: res.statusCode });
        });
        req.on('error', reject);
        req.end();
      });
    },
  });

  await server.start();
};

Best practices summary

  • Do not use user-controlled input to construct hostnames for TLS requests.
  • Pin the servername in TLS options and ensure it matches the certificate’s subject or SAN.
  • Validate resolved DNS results against an allowlist before use.
  • Keep client certificate verification enabled (requestCert + rejectUnauthorized) and ensure the CA bundle is up to date.

Frequently Asked Questions

Does middleBrick fix Dangling DNS findings in Hapi with Mutual TLS?
No. middleBrick detects and reports the issue with severity and remediation guidance. It does not fix, patch, block, or remediate. You must apply the code changes and configuration updates described in the findings.
Can the GitHub Action fail builds for high-severity DNS resolution issues in Hapi?
Yes. With the Pro plan, you can add the GitHub Action to your CI/CD pipeline and configure a threshold; if the scan’s risk score or specific finding severity exceeds the threshold, the action can fail the build.