HIGH dns cache poisoningexpressbearer tokens

Dns Cache Poisoning in Express with Bearer Tokens

Dns Cache Poisoning in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability

DNS cache poisoning (also known as DNS spoofing) occurs when an attacker injects forged DNS responses into a resolver’s cache, causing a domain to resolve to an attacker-controlled IP. In an Express application that relies on outbound HTTP requests to services discovered via DNS, poisoned cache can redirect traffic to a malicious host. When bearer tokens are used for authorization, this redirection can expose credentials and session material.

Consider an Express service that uses a hostname (e.g., api.partner.example) to reach an upstream API, sending requests with an Authorization: Bearer header. If the application does not pin the resolved IP or validate the server’s identity, a poisoned DNS entry can point the request to an attacker server that terminates TLS with a valid certificate for the hostname (e.g., via a compromised CA or a valid wildcard cert). Because the bearer token is attached automatically by the client, the malicious server can capture the token and replay it to impersonate the original service or escalate privileges.

The risk is compounded when Express is used in backend-to-backend flows where tokens are long-lived and permissions are broad. An attacker who controls DNS within the network or compromises a recursive resolver can intercept service discovery or API calls, leading to token theft and unauthorized access. This is a network-layer issue, but its impact is realized in the application layer when bearer tokens are transmitted to an unintended endpoint.

Bearer Tokens-Specific Remediation in Express — concrete code fixes

To reduce exposure, minimize the lifetime of bearer tokens in Express-based clients and avoid sending high-privilege tokens to hosts that cannot be strongly authenticated. Use short-lived tokens and refresh them using secure mechanisms. When calling external services, prefer explicit IP connections or certificate pinning where feasible, and validate the server’s identity before transmitting credentials.

Code example: Safe bearer token usage with short-lived access tokens

const express = require('express');
const axios = require('axios');
const qs = require('querystring');

const app = express();
app.use(express.json());

// Exchange a refresh token for a short-lived access token
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  if (!username || !password) {
    return res.status(400).json({ error: 'missing_credentials' });
  }

  try {
    const params = new URLSearchParams();
    params.append('grant_type', 'password');
    params.append('username', username);
    params.append('password', password);
    params.append('client_id', process.env.CLIENT_ID);
    params.append('client_secret', process.env.CLIENT_SECRET);

    const tokenResp = await axios.post('https://auth.example.com/oauth/token', params.toString(), {
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
    });

    res.json({
      access_token: tokenResp.data.access_token,
      expires_in: tokenResp.data.expires_in
    });
  } catch (err) {
    res.status(502).json({ error: 'auth_failure' });
  }
});

// Use short-lived access token to call a downstream service
app.get('/data', async (req, res) => {
  const accessToken = req.headers.authorization?.replace('Bearer ', '');
  if (!accessToken) {
    return res.status(401).json({ error: 'no_token' });
  }

  try {
    // Prefer explicit HTTPS endpoint; avoid relying on DNS at call time if you can pin IPs/certs
    const apiResponse = await axios.get('https://api.partner.example/v1/resource', {
      headers: { Authorization: `Bearer ${accessToken}` },
      httpsAgent: new (require('https').Agent)({ keepAlive: true }),
      timeout: 5000
    });
    res.json(apiResponse.data);
  } catch (err) {
    res.status(502).json({ error: 'upstream_error' });
  }
});

module.exports = app;

Code example: Centralized request wrapper with hostname verification

const axios = require('axios');
const https = require('https');
const crypto = require('crypto');

// Pin the expected certificate fingerprint for api.partner.example
const EXPECTED_FINGERPRINT = 'AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12';

function verifyFingerprint(res) {
  const peerCert = res.connection.getPeerCertificate();
  if (!peerCert || !peerCert.fingerprint) {
    throw new Error('missing_peer_certificate');
  }
  const fingerprint = peerCert.fingerprint.replace(/:/g, '').toUpperCase();
  const normalized = fingerprint.replace(/:/g, '').match(/.{1,2}/g).join(':').toUpperCase();
  if (normalized !== EXPECTED_FINGERPRINT) {
    throw new Error('fingerprint_mismatch');
  }
}

const client = axios.create({
  baseURL: 'https://api.partner.example',
  timeout: 8000,
  httpsAgent: new https.Agent({
    rejectUnauthorized: true,
    // In production, use a custom check that validates fingerprint or pinned public key
  })
});

client.interceptors.response.use((response) => {
  verifyFingerprint(response);
  return response;
}, (error) => {
  return Promise.reject(error);
});

// Example usage in an Express route
const express = require('express');
const app = express();

app.get('/protected', async (req, res) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ error: 'token_required' });

  try {
    const response = await client.get('/v1/secure', {
      headers: { Authorization: `Bearer ${token}` }
    });
    res.json(response.data);
  } catch (err) {
    res.status(502).json({ error: err.message || 'upstream_error' });
  }
});

module.exports = app;

Frequently Asked Questions

Can DNS cache poisoning affect token transmission even when TLS is used?
Yes. If an attacker redirects traffic to a server that presents a valid certificate for the expected hostname (e.g., via a compromised CA or a valid wildcard certificate), the TLS handshake will succeed and the bearer token can be captured. Always verify server identity beyond TLS.
Does middleBrick detect DNS-related risks in API scans?
middleBrick focuses on configuration and runtime behavior such as authentication, data exposure, and input validation. DNS cache poisoning is an infrastructure issue; ensure your resolver is hardened and use DNSSEC where possible. Use middleBrick to validate that bearer tokens are not unnecessarily exposed in logs or error messages.