Dns Rebinding in Actix
How DNS Rebinding Manifests in Actix
DNS rebinding attacks exploit the time gap between DNS resolution and request processing to bypass same-origin policy (SOP) or server-side request forgery (SSRF) protections. In Actix-web, this vulnerability typically arises when an application makes outbound HTTP requests based on user-controlled input (e.g., a target_url parameter) without validating that the resolved host remains within an allowed set after the initial connection.
Actix's default host validation middleware (actix-web::middleware::Logger or custom validators) often only checks the Host header of the incoming request. An attacker can craft a two-phase attack:
- Initial Request: The attacker's domain (e.g.,
attacker.com) resolves to a public IP, passing Actix's host whitelist (if configured to allow public domains). The server processes the request and stores the user-supplied URL for later use. - Subsequent Request: After the server has validated the host, the attacker's DNS record is changed to resolve to an internal IP (e.g.,
127.0.0.1,192.168.1.1). When Actix's HTTP client (oftenreqwest::Clientoractix_http::client::Client) later fetches the stored URL, it connects to the internal network, bypassing firewall rules that trust the server's outbound traffic.
A concrete vulnerable pattern in Actix looks like this:
use actix_web::{web, HttpResponse, Result};
use reqwest::Client;
async fn fetch_data(url: web::Json<UrlRequest>) -> Result {
let client = Client::new();
// User-controlled 'url' is used directly without re-validating the host
let response = client.get(&url.inner().target).send().await?;
Ok(HttpResponse::Ok().body(response.text().await?))
}
#[derive(Deserialize)]
struct UrlRequest {
target: String,
} Here, the target string from the JSON body is used directly. Even if the incoming request's Host header is validated, the outbound request's destination is not re-checked against a whitelist after DNS resolution. An attacker could submit { "target": "http://attacker.com/" }, have attacker.com resolve to 127.0.0.1:8080 (a local admin endpoint), and read its response.
This is particularly dangerous in Actix services that proxy requests or interact with internal microservices, as it can lead to:
- Access to cloud metadata endpoints (e.g., AWS
169.254.169.254) - Port scanning of internal networks
- Interaction with services like Docker daemon (
localhost:2375) or Kubernetes API server
Actix-Specific Detection
Detecting DNS rebinding vulnerabilities in Actix requires testing both the inbound host validation and the outbound request logic. Manual testing involves:
- Submitting a URL parameter that resolves to a private IP after initial validation
- Checking if the application fetches that URL and returns its content
- Observing if error messages leak internal IPs or service banners
Automated scanning with middleBrick identifies this via its SSRF and Input Validation checks. The scanner:
- Analyzes OpenAPI specs: If your Actix API has an OpenAPI definition, middleBrick cross-references parameters marked as
urloruriwith runtime behavior. - Performs active probing: middleBrick submits payloads like
http://127.0.0.1orhttp://[::1]in URL parameters and checks if the response contains indicators of internal service access (e.g.,"AWS_SECRET_ACCESS_KEY", Docker JSON, Kubernetes pod lists). - Tests DNS rebinding timing: The scanner uses a controlled domain that resolves to a public IP initially, then to
127.0.0.1after a short delay, mimicking a real rebinding attack.
To scan your Actix API with middleBrick:
# Using the CLI tool
middlebrick scan https://api.your-actix-app.com
# Or via the web dashboard at https://middlebrick.comThe resulting report will flag SSRF findings with severity based on the sensitivity of the internal service accessed. Findings map to OWASP API Top 10:API7:2023 – Server-Side Request Forgery (SSRF) and are linked to compliance frameworks like PCI-DSS (requirement 6.5.10) and SOC2 (CC7.1).
| Finding Category | middleBrick Check | OWASP API Top 10 | Example Actix Vulnerability |
|---|---|---|---|
| SSRF via DNS Rebinding | Outbound URL parameter validation | API7:2023 | Unvalidated client.get(url) from user input |
| Host Header Validation | Inbound Host header checks | API5:2023 (Broken Function Level Authorization) | Missing Host middleware whitelist |
Actix-Specific Remediation
Remediation in Actix requires a defense-in-depth approach: strict host whitelisting for inbound requests and rigorous validation of any outbound URLs. Use Actix's built-in middleware and Rust's type system to enforce boundaries.
1. Enforce Strict Host Validation Inbound
Actix provides actix-web::middleware::Host to whitelist allowed domains. Configure it early in your middleware chain:
use actix_web::{middleware::Logger, web, App, HttpServer, HttpResponse};
use actix_web::middleware::from_fn;
async fn validate_host(req: &HttpRequest) -> Result<(), Error> {
let allowed_hosts = ["api.yourdomain.com", "localhost:8080"];
let host = req.headers().get("Host").ok_or(ErrorBadRequest("Missing Host"))?;
if !allowed_hosts.iter().any(|&h| h == host.to_str().unwrap()) {
return Err(ErrorForbidden("Invalid Host"));
}
Ok(())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(from_fn(validate_host)) // Custom host validator
// .wrap(Host::new(["api.yourdomain.com"]).rewrite_invalid()) // Alternative built-in
.route("/fetch", web::post().to(fetch_data))
})
.bind("127.0.0.1:8080")?
.run()
.await
}The Host::new(whitelist).rewrite_invalid() middleware (available in actix-web 4+) rejects requests with Host headers not in the whitelist. However, this alone is insufficient for DNS rebinding—it only protects the initial request.
2. Validate Outbound URLs Rigorously
Never use raw user input in outbound clients. Parse the URL and validate its host against a strict allowlist:
use url::Url;
use reqwest::Client;
const ALLOWED_DESTINATIONS: [&str; 2] = ["internal-api:8080", "trusted-service:5000"];
async fn fetch_data(url: web::Json<UrlRequest>) -> Result {
let parsed_url = Url::parse(&url.inner().target)
.map_err(|_| ErrorBadRequest("Invalid URL"))?;
// Verify the host is in our allowlist (exact match or suffix for subdomains)
let host = parsed_url.host_str().ok_or(ErrorBadRequest("URL missing host"))?;
if !ALLOWED_DESTINATIONS.iter().any(|&allowed| {
host == allowed || host.ends_with(".".to_string() + allowed)
}) {
return Err(ErrorForbidden("Host not allowed"));
}
let client = Client::new();
let response = client.get(parsed_url).send().await?;
Ok(HttpResponse::Ok().body(response.text().await?))
} 3. Use Separate Clients with Restricted DNS Resolvers
For high-security contexts, configure the HTTP client to use a custom DNS resolver that blocks private IP ranges. With reqwest:
use reqwest::dns::{Addrs, Name, Resolve, Resolving};
use std::net::IpAddr;
struct PrivateBlockingResolver;
impl Resolve for PrivateBlockingResolver {
fn resolve(&self, name: Name) -> Resolving {
Box::pin(async move {
let mut addrs = Name::resolve(name).await?;
// Filter out private IPs (RFC 1918, loopback, link-local)
addrs.retain(|addr| match addr {
IpAddr::V4(ip) => !ip.is_private() && !ip.is_loopback() && !ip.is_link_local(),
IpAddr::V6(ip) => !ip.is_private() && !ip.is_loopback(),
});
Ok(addrs)
})
}
}
let client = Client::builder()
.dns_resolver(PrivateBlockingResolver)
.build()?;4. Consider Architectural Changes
If your Actix service must fetch arbitrary URLs (e.g., a URL shortener), isolate that functionality in a separate, network-segmented microservice with egress-only rules to the internet. Use environment variables or configuration files—not user input—to define allowed internal hosts.
Key Principle: Validate the URL's host after parsing, against a static allowlist, and never trust user-supplied domains for internal resource access. middleBrick's SSRF check will verify that your Actix endpoint does not accept private IPs or internal hostnames in URL parameters.
FAQ
- Does Actix's built-in Host middleware prevent DNS rebinding?
No. TheHostmiddleware only validates theHostheader of the incoming request. DNS rebinding attacks target the outbound request's destination, which is determined by DNS resolution that occurs after the initial request is accepted. You must also validate any user-supplied URLs used in outbound HTTP calls. - Is DNS rebinding only a browser-side vulnerability?
No. While originally a browser SOP bypass, DNS rebinding is equally critical for server-side applications like Actix services that make HTTP requests based on user input. An attacker can directly target your API endpoint with a malicious URL parameter, triggering the rebinding against your server's outbound client.