Arp Spoofing in Axum (Rust)
Arp Spoofing in Axum with Rust
Axum is a server-side web framework built on top of tower and hyper, designed for composing secure, modular web services in Rust. While Axum itself does not implement network-layer routing or ARP handling, its usage patterns can expose applications to ARP spoofing vulnerabilities when deployed in environments where service discovery or client communication occurs over untrusted networks. Specifically, when Axum applications expose RESTful APIs that are consumed by other services within the same network segment — such as microservices in a Kubernetes pod or internal HTTP clients — an attacker on the same LAN can perform ARP spoofing to intercept traffic between the Axum server and legitimate clients.
ARP spoofing exploits the stateless nature of the Address Resolution Protocol, which lacks built-in authentication. In a typical setup, a service behind an Axum server may rely on hostname resolution or direct IP communication to reach backend APIs. If the application does not validate the source of incoming requests at the network layer — such as by binding only to localhost or using TLS mutual authentication — an attacker can poison the ARP cache of a client host, causing it to send traffic intended for the Axum server to the attacker instead.
For example, consider an Axum application that runs on 0.0.0.0:8080 and is accessed internally by another service via hostname 'api.internal.local'. If DNS or mDNS is compromised or bypassed, and the client resolves the hostname to the attacker’s MAC address via spoofed ARP replies, all traffic flows through the attacker. This enables session hijacking, request forgery, or data exfiltration without breaking TLS — assuming the attacker does not terminate TLS.
This vulnerability is not unique to Axum but becomes relevant when Axum services are deployed without proper network segmentation or when client endpoints are not hardened against ARP cache poisoning. The framework provides tools for building secure APIs but cannot mitigate network-layer trust assumptions. Developers must assume responsibility for securing communication boundaries using infrastructure-level controls such as VLANs, bridge filtering, or ARP inspection.
Additionally, Axum applications that use HTTP clients (via hyper) to forward requests internally may inadvertently propagate spoofed responses if they do not verify response origin. For instance, a middleware that forwards requests between services without validating the source IP or TLS certificate can become a vector for man-in-the-middle attacks when combined with ARP spoofing.
In summary, ARP spoofing in Axum contexts arises not from framework flaws but from deployment misconfigurations that expose internal APIs to untrusted network participants. The combination of Rust’s compile-time safety and Axum’s explicit routing model helps reduce application-level bugs, but it does not eliminate network-layer risks that require architectural safeguards.
Rust-Specific Remediation in Axum
To remediate ARP spoofing risks in Axum applications, developers must enforce strict network boundaries and validate communication endpoints at the application level. Since Axum runs on top of Tokio and hyper, secure configurations can be implemented using binding restrictions, client validation, and middleware that enforces trusted source checks.
First, bind the Axum server to localhost or a private interface only when appropriate, avoiding 0.0.0.0 in development or internal environments unless protected by network controls:
use axum::{routing::get, Router};
use std::net::SocketAddr;
async fn health_handler() -> &'static str {
"OK"
}
#[tokio::main]
async fn main() {
// Bind only to localhost for development; use private IP in production
let addr = SocketAddr::V4(std::net::Ipv4Addr::LOCALHOST);
let app = Router::new().route("/health", get(health_handler));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await;
}
This prevents external hosts from reaching the server directly. For internal services, bind to a specific private IP or use a Unix domain socket to limit exposure.
Second, when using Axum to forward requests between services, validate the source of incoming requests. For example, if a service receives forwarded requests from another internal API, it should verify the request originates from a known client IP or use a shared secret:
use axum::{extract::ConnectInfo, routing::post, Router, http::StatusCode};
use std::net::SocketAddr;
async fn forward_handler(
ConnectInfo(remote): ConnectInfo,
body: axum::body::Body,
) -> Result {
// Allow only requests from known internal IPs
let allowed_ips = ["192.168.1.10", "192.168.1.11"];
if !allowed_ips.contains(&remote.ip().to_string().as_str()) {
return Err(StatusCode::FORBIDDEN);
}
// Forward logic here
Ok("forwarded".to_string())
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/forward", post(forward_handler));
axum::Server::bind(&SocketAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)))
.serve(app.into_make_service())
.await;
}
Third, use mutual TLS (mTLS) between services to ensure authentication even if ARP is compromised. While Axum does not enforce mTLS by default, it can be integrated with tower-http to validate client certificates:
use axum::{Router, routing::get};
use tower_http::migration::TransportLayer;
use tower_http::tls::TlsAcceptor;
use std::sync::Arc;
async fn secure_endpoint() -> &'static str {
"secure"
}
#[tokio::main]
async fn main() {
let tls = TlsAcceptor::from("servercert.pem").accept();
let app = Router::new().route("/secure", get(secure_endpoint));
let app = app.layer(TransportLayer::new(Arc::new(tls)));
axum::Server::bind(&SocketAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)))
.serve(app.into_make_service())
.await;
}
Finally, combine these practices with network infrastructure: enable port security, use static ARP entries for critical services, and deploy ARP inspection on managed switches. Axum applications should be designed under the assumption that network-layer trust is fragile, and defensive coding practices should reflect that.