DNS Rebinding Hardening

DNS rebinding hardening

A loopback HTTP server is a juicy target because the browser will happily make requests to 127.0.0.1 from any page the user visits. The classic attack is DNS rebinding: a malicious page resolves attacker.com to 127.0.0.1 after the user loads it, then issues same-origin requests against the local daemon. The defense is three layers, none sufficient alone.

The three layers

  1. Bearer auth required on every route except /health — a page without the token can't do anything useful even if it reaches the bridge. The /health exception is loud and read-only; nothing else is.
  2. Host-header allowlistlocalhost, mxr.localhost, 127.0.0.1, [::1] are accepted; everything else returns 403. DNS rebinding requests arrive with Host: attacker.com, which the allowlist rejects before the route handler runs.
  3. CORS allowlist — only loopback origins by default. Cross-origin requests from other domains fail at the preflight, so the malicious page can't even ask.

Any one layer caught alone is bypassable: bearer auth alone leaks the token to a rebinding page that knows where to look; host-header alone fails if the attacker can spoof the Host (they often can in dev setups); CORS alone misses non-CORS-protected requests. All three together is what makes the surface inert to the realistic attack tree.

The 404-over-401 trick for sensitive endpoints

Some endpoints exist only for loopback peers (mxr's /api/v1/auth/local-token returns the bridge token to same-machine callers). For these, return 404, not 401 or 403, when the request comes from a non-loopback peer.

The difference: 401/403 confirms the endpoint exists. A cross-network scanner gets back "this endpoint exists, you just can't access it." 404 says "no such endpoint here." Scanners can't enumerate what they can't see.

The cost is one branch and one status code. The benefit is that the attacker's reconnaissance turns up nothing. This is a small specific case of the principle: when an endpoint is conditional on caller identity, hide the endpoint itself, not just the data.

What this leaves on the table

Non-loopback binds (0.0.0.0) need TLS termination and a different threat model. The hardening here doesn't cover that case — the bridge refuses to bind non-loopback without explicit TLS setup, deferring the problem to the operator who'll configure a reverse proxy with TLS in front.

This is a defensible deferral for a single-user local daemon. It would not be defensible for a multi-user product.

Where this generalizes

Any local-machine HTTP server hosting capability that the user wouldn't want a random webpage to invoke:

The same three layers apply. Bearer auth, host-header allowlist, CORS allowlist. The 404-trick applies to any loopback-only endpoint. If your local server doesn't have all three, a webpage the user visits while the server is running can probably do interesting things to it.

See also