HIGH axumrustdns rebinding

Dns Rebinding in Axum (Rust)

Dns Rebinding in Axum with Rust — how this specific combination creates or exposes the vulnerability

DNS rebinding is a client-side attack where a malicious webpage causes a visitor’s browser to bypass same-origin policy by changing the DNS resolution of a domain from a public IP to a private IP after the page loads. When an API built with the Rust web framework Axum does not enforce strict source validation, an attacker may use rebinding to make a browser-embedded script call internal endpoints that were assumed to be inaccessible from the internet. Because Axum is often used to serve both public API surfaces and internal management interfaces on the same host, the risk is elevated if routes are not properly segregated and requests are not validated at the edge.

In Axum, this typically occurs when an application relies on host-based trust or permissive route matching without verifying the resolved destination of incoming requests. For example, if an app defines a route like /admin and assumes it will only be reached via a protected host or internal network, an attacker can craft a DNS response that resolves the public hostname to 127.0.0.1 after the initial page load, causing the victim’s browser to send requests directly to the localhost interface. Because Axum processes these requests in the same runtime as public traffic, the server may inadvertently expose internal handlers or sensitive operations to the client-side script.

Rust’s type system and Axum’s extractor model provide strong compile-time guarantees, but they do not automatically prevent rebinding. The framework does not inspect the network-level source of a request beyond what is presented by the underlying HTTP service. If the application uses host headers for access control, an attacker can bypass checks by switching DNS resolution mid-session. Additionally, if Axum routes are defined with broad path patterns or if the service listens on multiple interfaces (e.g., 0.0.0.0), the server may accept connections from loopback or private ranges that should be restricted at the network or middleware layer.

To detect this class of issue, scanners like middleBrick perform unauthenticated black-box testing against the public endpoint, attempting to trigger rebinding sequences and observing whether responses reveal internal behavior or bypass intended access controls. When Axum applications inadvertently expose administrative or data endpoints through relaxed binding rules, these checks can surface findings related to BOLA/IDOR and improper access control, even when the code appears correct at the route level.

Concrete mitigation requires validating that incoming requests originate from intended network boundaries and not just from a trusted hostname. This includes binding listeners to specific interfaces, enforcing strict host header validation, and using network-level restrictions where possible. Middleware that inspects the X-Forwarded-For header or connection metadata can complement route design, but must be implemented with care to avoid introducing new attack surfaces.

Rust-Specific Remediation in Axum — concrete code fixes

Securing an Axum application against DNS rebinding in Rust begins with explicit network binding and rigorous request validation. Instead of relying on default host matching or assuming loopback safety, define a strict source validation layer that runs before routing logic. Below are concrete, idiomatic examples that demonstrate how to implement these controls.

First, bind the HTTP listener to a specific interface rather than 0.0.0.0. This reduces the attack surface by ensuring the server does not accept connections from unexpected network locations.

use std::net::SocketAddr;
use axum::Server;

#[tokio::main]
async fn main() {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); // bind to localhost only
    let app = axum::Router::new()
        .route("/health", axum::routing::get(|| async { "ok" }));

    Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Second, implement a middleware that validates the Host header and rejects requests where the resolved destination does not match an expected set of values. This prevents browsers under rebinding control from issuing requests that appear to come from a valid host but resolve internally.

use axum::{http::HeaderValue, extract::Request, middleware::Next};
use std::task::Poll;

async fn host_header_middleware(
    request: Request,
    next: Next,
) -> Result {
    let host = request.headers()
        .get("host")
        .and_tokio::task::spawn_blocking(move || {
            // compare against allowed hosts
            match host.to_str() {
                Ok("api.example.com") | Ok("staging.example.com") => Ok(next.run(request).await),
                _ => Err((axum::http::StatusCode::FORBIDDEN, "invalid host".into())),
            }
        })
        .await
        .map_err(|_| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "host check failed".into()))?;

    host
}

Third, avoid using hostname-based routing for privilege separation. Instead, segregate public and internal APIs using distinct paths and apply extractor-level guards that validate authentication and authorization independently of DNS or host state.

use axum::{routing::get, Router};

let public_router = Router::new()
    .route("/public/data", get(public_data_handler));

let admin_router = Router::new()
    .route("/admin/settings", get(admin_settings_handler))
    .layer(axum::middleware::from_fn(auth_middleware));

let app = public_router.merge(admin_router);

Finally, when deploying behind proxies, ensure that X-Forwarded-For and X-Forwarded-Host are not trusted without verification. Use Axum extractors to inspect connection information when running on a gateway, and combine this with explicit allowlists for internal traffic.

use axum::extract::ConnectInfo;
use std::net::IpAddr;

async fn validate_source(
    ConnectInfo(addr): ConnectInfo,
) -> Result<(), (axum::http::StatusCode, String)> {
    if addr.ip().is_loopback() {
        Ok(())
    } else {
        Err((axum::http::StatusCode::FORBIDDEN, "external access to admin route".into()))
    }
}

By combining strict interface binding, host validation, route segregation, and connection-aware extractors, Rust developers using Axum can effectively neutralize DNS rebinding risks while preserving the language’s safety guarantees.

Frequently Asked Questions

Can DNS rebinding affect APIs that require authentication?
Yes. If an authenticated session is established and the attacker forces the browser to rebind to an internal IP, requests can inherit the same credentials, bypassing network-level assumptions about trust.
Does middleBrick detect DNS rebinding during scans?
middleBrick tests unauthenticated attack surfaces and can identify endpoints that are vulnerable to source validation bypass, including patterns associated with DNS rebinding.