Dns Cache Poisoning in Axum with Basic Auth
Dns Cache Poisoning in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability
DNS cache poisoning (also known as DNS spoofing) occurs when an attacker injects fraudulent DNS records into a resolver’s cache, causing clients to be redirected to malicious infrastructure. When an Axum service protected with Basic Auth resolves hostnames at runtime—such as during backend calls, service discovery, or OAuth token endpoints—its behavior depends on how and where DNS resolution happens. If Axum (or the runtime/VM it runs on) relies on the operating system resolver or a library that does not enforce strict DNS validation, poisoned cache entries can redirect requests to an attacker-controlled host.
Basic Auth does not prevent DNS cache poisoning; it only protects the credentials over the transport if TLS is correctly enforced. The risk emerges in scenarios where an Axum application uses resolved hostnames for downstream calls after successful Basic Auth validation. For example, an authenticated request might trigger a server-side call to an internal service via a hostname that could be poisoned. If TLS is missing or improperly validated (e.g., certificate checks are bypassed), the client may unknowingly send sensitive credentials and session data to a malicious server. Additionally, if the Axum service itself acts as a resolver or makes HTTP requests based on hostnames provided by clients (which should never be trusted), poisoned DNS can lead to SSRF-like redirection where authentication headers are forwarded to attacker infrastructure.
Consider an Axum API that, after validating Basic Auth, calls an external OAuth discovery endpoint using a hostname obtained from configuration or user input. If that hostname’s DNS record is poisoned to point to an attacker server, the API may perform TLS handshakes with the attacker. Should the Axum code skip certificate validation—explicitly or implicitly—credentials and tokens can be exfiltrated. Even with strict TLS, poisoned DNS can lead to service disruption by redirecting traffic, causing availability issues that complement the confidentiality breach. Therefore, the combination of Axum, Basic Auth, and unsafe DNS resolution increases the impact of cache poisoning by exposing authenticated sessions and sensitive backend communications to interception or redirection.
Basic Auth-Specific Remediation in Axum — concrete code fixes
Mitigation focuses on preventing DNS cache poisoning vectors and ensuring that Basic Auth credentials are not exposed to redirected or malicious endpoints. Below are concrete Axum patterns that reduce risk.
- Use IP addresses for critical backend calls instead of hostnames to avoid DNS resolution at runtime. If hostnames are required, pin certificates and enforce strict validation.
- Disable client redirects and do not follow HTTP location headers automatically when using HTTP clients.
- Pin server certificates or public key hashes (pinning) for endpoints that handle Basic Auth after authentication.
- Ensure TLS with strong cipher suites and reject untrusted or self-signed certificates even in development builds.
- Validate and sanitize any user-supplied input used in URLs or host resolution; do not allow clients to dictate backend targets.
Example: Axum route with Basic Auth that calls a pinned backend using an IP address and strict HTTPS verification.
use axum::{routing::get, Router, http::HeaderValue};
use hyper::{Client, Uri};
use hyper_rustls::HttpsConnector;
use std::convert::Infallible;
use tower_http::auth::{AuthLayer, Credentials, ServiceAuthenticationLayer};
async fn handler() -> String {
// Prefer IP-based backend calls to avoid DNS dependency
"Authenticated access granted".to_string()
}
#[tokio::main]
async fn main() {
let addr = "0.0.0.0:3000".parse().unwrap();
// Basic Auth extractor
let auth_layer = AuthLayer::basic(vec![("user", "correct-hashed-password")]);
let app = Router::new()
.route("/secure", get(handler))
.layer(auth_layer);
// Example of a pinned HTTPS client using IP to avoid DNS poisoning
let https = HttpsConnector::new();
let client: Client> = Client::builder().build(https);
// If you must call a hostname, pin the certificate and avoid user-supplied hostnames
// For production, load pins from a secure configuration
let backend_uri: Uri = "https://192.0.2.1:8443/endpoint".parse().expect("Valid URI");
// Do not follow redirects automatically
let client_no_redirect = Client::builder()
.redirect(hyper::client::RedirectPolicy::none())
.build(https);
axum::Server::bind(&addr)
.serve(app.into_make_service())}
Example: Axum route that rejects untrusted TLS and avoids unsafe hostname resolution.
use axum::Router;
use hyper::{Body, Request, Response};
use hyper_rustls::{ConfigBuilderExt, HttpsConnector, TlsAcceptor};
use std::sync::Arc;
use tokio_rustls::rustls::{self, ServerConfig};
async fn api_endpoint() -> &'static str {
"Data retrieved securely"
}
fn build_rustls_config() -> Arc {
let mut config = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(vec![], rustls::PrivateKey(vec![])) // load real cert/key in production
.expect("Invalid certificate or key");
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
Arc::new(config)
}
#[tokio::main]
async fn main() {
let tls_config = build_rustls_config();
let acceptor = TlsAcceptor::from(tls_config);
let https = HttpsConnector::with_tls_acceptor(acceptor).expect("Valid TLS acceptor");
let client = hyper::Client::builder().build(https);
// Ensure client does not accept invalid certificates
let app = Router::new().route("/data", get(api_endpoint));
// Bind and serve with pinned TLS and no unsafe redirects
let addr = "0.0.0.0:8443";
axum::Server::bind(&addr)
.https_only(true)
.build_rustls(acceptor)
.serve(app.into_make_service())}