Arp Spoofing in Actix with Jwt Tokens
Arp Spoofing in Actix with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Arp spoofing is a Layer 2 attack in which an attacker sends falsified Address Resolution Protocol replies to associate their MAC address with the IP address of a legitimate host, typically the gateway or another API server. When this occurs in an Actix-based application that relies on JWT tokens for authentication, the attack surface changes in a way that does not directly break JWT validation but undermines the trust assumptions that allow JWT verification to happen safely.
Consider an Actix web service that validates JWT tokens on each request. The service may call an internal authentication endpoint or rely on a network-routable identity provider to perform signature verification or to fetch key material (for example, using a JWKS endpoint). If an attacker conducts arp spoofing on the network segment hosting the Actix service or its upstream authentication service, they can intercept or redirect traffic between the service and the identity provider. Even though the JWT remains cryptographically intact, the Actix application may unknowingly communicate with a malicious host that presents a valid-looking public key or a forged introspection response. This can lead to acceptance of tokens that should be rejected, or to token leakage when responses intended for the legitimate service are redirected.
Importantly, arp spoofing does not bypass JWT signature verification if the verification logic and keys are correctly implemented locally. However, it can weaken the chain of custody for secrets and keys. For example, if the Actix application fetches signing keys over HTTP from a discovery endpoint rather than HTTPS, an attacker can use arp spoofing to serve a malicious JWKS that maps the expected key ID to an attacker-controlled private key. The tokens will then validate successfully, but with keys controlled by the attacker. Similarly, if the Actix service uses token introspection over HTTP, arp spoofing can allow the attacker to craft false introspection responses indicating that a token is valid when it is not. These outcomes are not weaknesses in the JWT format itself, but result from placing trust on network-layer routing in an environment where layer 2 security cannot be guaranteed.
These concerns are especially relevant in deployment environments where multiple services share a flat network, such as containers on a Kubernetes node or microservices in the same virtual network. The Actix runtime should assume that network visibility and integrity cannot be relied upon, even when communication appears to stay within a private subnet. JWT usage in such contexts must minimize dependency on remote key discovery and must enforce strict host verification and certificate pinning for any HTTPS calls used in token validation workflows. Without such safeguards, arp spoofing creates a path to undermine the effective security of JWT-based authentication in Actix applications, even if the token format and signature algorithms remain correct.
Jwt Tokens-Specific Remediation in Actix — concrete code fixes
Defending against arp spoofing when using JWT tokens in Actix centers on ensuring that all cryptographic operations and key material are obtained over authenticated and encrypted channels, and that the application does not implicitly trust network-level routing. Below are concrete remediation steps and code examples that you can apply in an Actix web service.
First, always fetch signing keys over HTTPS and verify the server identity. If you use a JWKS endpoint, pin the host and validate certificates rather than relying on HTTP or accepting any TLS certificate. Here is an example of fetching a JWKS over HTTPS with strict host verification using reqwest and ring in Actix runtime code:
use reqwest::Client;
use serde_json::Value;
use std::collections::HashMap;
use ring::signature::UnparsedPublicKey;
use ring::signature::ED25519;
async fn fetch_jwks() -> Result<HashMap<String, Vec<u8>>, Box<dyn std::error::Error>> {
let client = Client::builder()
.https_only(true)
.danger_accept_invalid_certs(false)
.build()?;
// Pin the expected hostname at the application layer
let resp = client
.get("https://auth.example.com/.well-known/jwks.json")
.send()
.await?;
// Ensure the response hostname matches the pinned name
if resp.url().host_str() != Some("auth.example.com") {
return Err("Unexpected host".into());
}
let body: Value = resp.json().await?;
let keys = body.get("keys")
.and_then(|v| v.as_array())
.ok_or("missing keys")?;
let mut map = HashMap::new();
for key in keys {
if let (Some(kid), Some(x5c)) = (key.get("kid").and_then(|v| v.as_str()), key.get("x5c").and_then(|v| v.as_array()).and_then(|a| a.first()).and_then(|v| v.as_str())) {
let der = base64::decode_config(x5c, base64::URL_SAFE_NO_PAD)?;
map.insert(kid.to_string(), der);
}
}
Ok(map)
}
Second, validate tokens locally using keys obtained from the trusted source, and avoid runtime key selection based on unverified headers. Use a verified library such as jsonwebtoken with strict algorithms and do not accept "none" algorithms. Example token validation in an Actix extractor:
use actix_web::{dev::Payload, FromRequest, HttpRequest, web, Error};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation, TokenData};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
exp: usize,
// add custom claims
}
struct JwtAuth {
claims: Claims,
}
impl FromRequest for JwtAuth {
type Error = Error;
type Future = std::future::Ready<Result<Self, Self::Error>>;
type Config = ();
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let token = req.headers().get("Authorization")
.and_then(|h| h.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "))
.unwrap_or("");
// Use a locally cached, pinned key rather than dynamic key selection
let key = load_local_pinned_key();
let validation = Validation::new(Algorithm::RS256);
match decode::
Third, enforce transport security across all service-to-service communication within your deployment. Use mTLS where feasible so that even if an attacker succeeds in ARP spoofing, they cannot decrypt or meaningfully alter authenticated requests. In Actix, you can require client certificates for inbound routes:
use actix_web::{web, App, HttpServer};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
builder.set_private_key_file("server.key", SslFiletype::PEM)?;
builder.set_certificate_chain_file("server.crt")?;
builder.set_client_ca_file("client-ca.crt")?;
builder.set_verify(openssl::ssl::SslVerifyMode::PEER | openssl::ssl::SslVerifyMode::FAIL_IF_NO_PEER_CERT, None);
HttpServer::new(|| {
App::new()
.route("/api/token-info", web::get().to(token_info))
})
.bind_openssl("120.0.0.1:8443", builder)?
.run()
.await
}
Finally, minimize the use of remote key discovery in favor of locally pinned keys and rotate keys regularly. Combine these practices with network-level hardening, such as using VPCs or network policies that limit unnecessary lateral movement, to reduce the impact of arp spoofing on JWT-based authentication in Actix services.