MEDIUM dangling dnsaxumrust

Dangling Dns in Axum (Rust)

Dangling DNS in Axum with Rust — how this specific combination creates or exposes the vulnerability

Dangling DNS occurs when an API endpoint references a domain in its configuration (e.g., for external service calls, webhook URLs, or redirect logic) that points to a DNS record which no longer resolves to an owned or controlled resource. In the context of Axum, a Rust-based web framework, this vulnerability often arises in middleware, route handlers, or state shared across requests where domain names are dynamically resolved or used in outbound HTTP calls without validation.

For example, consider an Axum handler that forwards requests to a third-party service based on a user-provided subdomain, using a base domain configured at startup. If the DNS record for that subdomain is deleted or expires (e.g., a marketing subdomain like promo.example.com pointing to a now-decommissioned external service), an attacker can register the dangling domain and intercept or manipulate traffic intended for the original service.

In Axum applications, this risk is heightened when:

  • Configuration values (like BASE_EXTERNAL_URL) are loaded once at startup and never re-validated.
  • Outbound HTTP clients (e.g., using reqwest) are constructed with string-concatenated URLs that include user-influenced host parts.
  • Redirects or webhook validations rely on domain matching without checking DNS ownership or using allowlists.

Unlike server-side flaws such as SQL injection, dangling DNS is a runtime dependency issue. Axum’s async, type-safe nature doesn’t prevent it because the vulnerability lies in external trust assumptions, not code logic. The framework’s strength in compile-time safety does not extend to runtime DNS validity, making configuration hygiene critical.

Real-world parallels include CVE-2021-30465 (Azure Cloud Shell domain takeover) and OWASP API Security Top 10 2023’s A01:2023 – Broken Object Property Authorization, where trust in external references leads to indirect access control bypass. middleBrick detects such risks by scanning for outbound calls to unresolved or externally controllable domains during its unauthenticated attack surface analysis, flagging them under 'Data Exposure' or 'Unsafe Consumption' categories.

Rust-Specific Remediation in Axum — concrete code fixes

Mitigating dangling DNS in Axum requires validating external domains at runtime and avoiding dynamic trust in DNS-resolved endpoints. Rust’s ownership and type system enable compile-time guarantees for configuration handling, but runtime validation must be explicit.

First, avoid hardcoding or concatenating user input into URLs for outbound requests. Instead, use structured configuration with validation at load time. For example, use the config crate to load and validate settings:

use config::{Config, File};
use std::net::IpAddr;

#[derive(Debug, serde::Deserialize)]
struct Settings {
    external_api_base: String, // e.g., "https://api.trustedpartner.com"
}

impl Settings {
    fn validate(&self) -> Result<(), String> {
        // Parse URL and validate host is IP or allowlisted domain
        let url = url::Url::parse(&self.external_api_base)
            .map_err(|e| format!("Invalid URL: {}", e))?;
        
        let host = url.host_str().ok_or_else(|| "Missing host".to_string())?;
        
        // Option 1: Allowlist known domains
        let allowed = ["api.trustedpartner.com", "webhook.trustedpartner.com"];
        if !allowed.iter().any(|&d| host == d || host.ends_with(&format!(".{}", d))) {
            return Err(format!("Host '{}' not in allowlist", host));
        }
        
        // Option 2: Reject IPs to prevent SSRF via DNS rebinding (optional)
        if host.parse::().is_ok() {
            return Err("IP addresses not allowed in external API base".to_string());
        }
        
        Ok(())
    }
}

let settings = Config::builder()
    .add(File::with_name("Settings"))
    .build()?
    .try_deserialize::()?;

settings.validate()?; // Fail fast if misconfigured

Second, when making outbound requests (e.g., with reqwest), isolate the HTTP client and validate hosts per request if dynamic values are unavoidable:

use axum::extract::State;
use axum::http::StatusCode;
use reqwest::Url;

async fn forward_request(
    State(client): State,
    State(settings): State,
    axum::Json(payload): axum::Json,
) -> Result, (StatusCode, String)> {
    let target_url = format!("{}/webhook", settings.external_api_base);
    
    let parsed = Url::parse(&target_url)
        .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid target URL: {}", e)))?;
    
    // Re-validate host at request time (defense in depth)
    let host = parsed.host_str().ok_or_else(|| (
        StatusCode::INTERNAL_SERVER_ERROR,
        "Missing host in target URL".to_string(),
    ))?;
    
    if !["api.trustedpartner.com"].contains(&host) {
        return Err((
            StatusCode::FORBIDDEN,
            format!("Outbound request to unauthorized host: {}", host),
        ));
    }
    
    let response = client
        .post(parsed.as_str())
        .json(&payload)
        .send()
        .await
        .map_err(|e| (StatusCode::BAD_GATEWAY, e.to_string()))?;
    
    let body = response.json().await.map_err(|e| (StatusCode::BAD_GATEWAY, e.to_string()))?;
    
    Ok(axum::Json(body))
}

Finally, leverage middleBrick’s scanning to detect risky outbound calls in your Axum API. Submit your public URL via the dashboard, CLI (middlebrick scan https://your-api.com), or GitHub Action to identify dangling DNS risks under 'Unsafe Consumption' or 'Data Exposure' findings, with remediation guidance pointing to validate or allowlist external domains.

Frequently Asked Questions

Can middleBrick detect dangling DNS risks in Axum applications without accessing source code or configuration?
Yes. middleBrick performs black-box scanning of the unauthenticated attack surface. It identifies outbound HTTP requests to domains during active testing (e.g., via parameter analysis or endpoint probing) and checks whether those domains resolve to IP addresses owned by the applicant. If a domain points to an uncontrolled or unresolvable IP, it is flagged as a potential dangling DNS risk under findings like 'Unsafe Consumption' or 'Data Exposure', without needing source code, agents, or credentials.
Does using Rust’s ownership model prevent dangling DNS vulnerabilities in Axum?
No. Rust’s ownership and borrowing system prevents memory safety issues (e.g., use-after-free, data races) at compile time, but dangling DNS is a runtime configuration and trust issue. It depends on external DNS validity and how the application uses resolved domains in outbound requests. Safe Rust code can still construct and use URLs pointing to dangling domains if configuration is not validated or if user input influences host selection without checks.