HIGH dns rebindingfastapihmac signatures

Dns Rebinding in Fastapi with Hmac Signatures

Dns Rebinding in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability

DNS rebinding is a client-side network attack that manipulates DNS responses to make a victim’s browser believe a malicious server is actually a trusted internal host. When combined with FastAPI endpoints that rely on HMAC signatures for request authentication, this technique can bypass origin validation if the application does not adequately guard against resolved IP address changes during a single request lifecycle.

Consider a FastAPI service that uses HMAC signatures to verify request integrity. The server might validate a signature using a shared secret and expected headers, but if it resolves the client IP only once and uses that value in a security decision without rechecking the resolved origin, an attacker can exploit DNS rebinding to change the apparent source IP after the initial validation phase. For example, an endpoint may verify a signature on an incoming POST, confirm the IP matches an allowlist, and then process sensitive actions. An attacker can craft a page that first resolves the domain to the allowed IP, passes the HMAC check, and then rebinds to a different internal IP, attempting to reuse the same signed request or trick the server into forwarding or logging sensitive data to the attacker-controlled machine.

In practice, this can surface when FastAPI applications call request.client.host early in middleware or route logic and cache it for later authorization checks. Because DNS resolution occurs at the network layer and can return different IPs over time or within a short window, the cached IP becomes untrustworthy. Attackers use domains that resolve to an IP inside the target network, then rebind to another internal host, attempting to exploit trust placed in IP-based controls. If HMAC verification does not incorporate protections such as strict host validation, nonce usage, or short-lived timestamps, the signature may remain valid even when the network path changes, enabling request smuggling or unauthorized replay within the perceived trusted zone.

To detect this class of issue, scanning tools evaluate whether FastAPI endpoints that use HMAC signatures also validate the request target’s identity beyond the initial signature check. They look for missing or weak anti-rebinding controls such as per-request nonces, timestamps with tight windows, or explicit host verification that survives DNS changes. Without these measures, an API that appears to authenticate requests via HMAC may still be vulnerable to abuse when DNS rebinding is possible, because the effective origin of the request can shift in ways the application does not reassess.

Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes

Defending against DNS rebinding in FastAPI when using HMAC signatures requires ensuring that each authenticated request validates not only the cryptographic integrity of the message but also the ongoing identity of the sender and the request context. Below are concrete remediation patterns with syntactically correct code examples.

1. Include a timestamp and nonce in the signed payload

Bind the request time and a one-time value into the HMAC calculation to prevent replay across time windows. Verify both freshness and uniqueness on the server.

from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import hmac
import hashlib
import time
import os

app = FastAPI()
security = HTTPBearer()

# Shared secret stored securely, e.g., from secrets manager
SHARED_SECRET = os.environ.get("HMAC_SECRET", "super-secret-key-change-in-prod").encode()
TIMESTAMP_TOLERANCE_SECONDS = 300  # 5 minutes

def verify_hmac_signature(
    payload: bytes,
    signature_header: str,
    received_timestamp: int,
    received_nonce: str
) -> bool:
    # Check timestamp freshness
    now = int(time.time())
    if abs(now - received_timestamp) > TIMESTAMP_TOLERANCE_SECONDS:
        return False
    # Reconstruct signing string
    message = f"{received_timestamp}.{received_nonce}.{payload.decode()}"
    expected_signature = hmac.new(SHARED_SECRET, message.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected_signature, signature_header)

@app.post("/secure-action")
async def secure_action(
    request: Request,
    credentials: HTTPAuthorizationCredentials = Depends(security)
):
    body = await request.body()
    timestamp = int(request.headers.get("X-Timestamp", "0"))
    nonce = request.headers.get("X-Nonce", "")
    signature = request.headers.get("X-Signature", "")

    if not nonce or not verify_hmac_signature(body, signature, timestamp, nonce):
        raise HTTPException(status_code=401, detail="Invalid signature or replay attempt")

    # Proceed with request handling; you may also re-validate request.client.host if using IP controls
    return {"status": "ok"}

2. Validate the request target host against the TLS SNI or Host header

Ensure the hostname the client intended to reach matches expectations, and do not rely solely on the first resolved IP. Compare the Host header and, where available, the TLS server name indication.

from fastapi import Request, HTTPException
from urllib.parse import urlparse

EXPECTED_DOMAIN = "api.yourcompany.com"

def validate_request_origin(request: Request) -> bool:
    # Host header validation
    host = request.headers.get("host", "")
    if not host.startswith(EXPECTED_DOMAIN):
        return False
    # Optionally verify TLS SNI if terminating TLS at an edge that exposes it
    # (not always present in FastAPI directly; handled upstream)
    return True

@app.middleware("http")
async def verify_origin_middleware(request: Request, call_next):
    if not validate_request_origin(request):
        raise HTTPException(status_code=403, detail="Origin validation failed")
    response = await call_next(request)
    return response

3. Use short-lived tokens or session identifiers bound to the connection context

Issue a one-time token tied to an initial handshake that includes the resolved address at that time, and require it for subsequent actions. This limits the window for rebinding attacks. While this does not replace HMAC for request integrity, it adds a second factor that is sensitive to network path changes.

# Example token generation during an initial handshake
import secrets
session_tokens: dict[str, dict] = {}

def generate_handshake_token(client_fingerprint: str) -> str:
    token = secrets.token_urlsafe(32)
    session_tokens[token] = {
        "fingerprint": client_fingerprint,
        "created_at": int(time.time())
    }
    return token

@app.get("/handshake")
async def handshake(request: Request):
    fingerprint = f"{request.client.host}:{request.url.port}"
    token = generate_handshake_token(fingerprint)
    return {"token": token}

@app.post("/action-with-token")
async def action_with_token(request: Request, token: str):
    if token not in session_tokens:
        raise HTTPException(status_code=401, detail="Invalid session token")
    # Optionally re-check fingerprint or IP if required by policy
    del session_tokens[token]  # single-use token
    return {"status": "ok"}

General operational practices

  • Do not cache request.client.host for authorization across multiple checks within a request lifecycle.
  • Enforce short timestamp tolerances (e.g., 5 minutes or less) and single-use nonces to mitigate replay and rebinding.
  • Combine HMAC verification with explicit host and, where feasible, TLS identity checks to reduce reliance on IP-based trust alone.

Frequently Asked Questions

Can DNS rebinding bypass HMAC signatures if timestamps and nonces are not used?
Yes. Without per-request timestamps and nonces, a signed request may be reused after the apparent source IP changes via DNS rebinding, allowing an attacker to pass HMAC checks with a manipulated network path.
Does validating the Host header alone fully prevent DNS rebinding in FastAPI with HMAC signatures?
It helps, but is not sufficient alone. Combine Host header validation with short-lived nonces, timestamps, and re-evaluation of request context on each sensitive operation to reduce rebinding risk.