Dns Cache Poisoning in Axum with Jwt Tokens
Dns Cache Poisoning in Axum with Jwt Tokens — how this specific combination creates or exposes the vulnerability
In an Axum application that relies on JWT tokens for authentication, DNS cache poisoning can undermine the entire security model by redirecting API or identity provider endpoints to attacker-controlled hosts. When an Axum service resolves a hostname such as auth.example.com at startup or at runtime, a poisoned cache can cause the application to establish TLS sessions and exchange JWT tokens with a malicious server. This exposure is particularly dangerous because JWT tokens often contain sensitive claims and are transmitted in HTTP headers; intercepting or altering those tokens can lead to token theft or token injection.
The vulnerability arises when Axum-based services perform hostname verification without strict pinning or when they resolve endpoints dynamically without validating the underlying transport integrity. For example, if an Axum application obtains discovery information from a compromised DNS entry, the redirect may point to a server controlled by an attacker who can terminate TLS and present a valid certificate for the target domain. The application may then accept JWT tokens issued by that rogue server, effectively bypassing identity checks. Additionally, if the application caches DNS lookups and does not re-validate the source of discovery data, the poisoned entry can persist across restarts, prolonging the window for token interception or manipulation.
Another angle involves SSRF and supply-chain risks: an attacker who can poison DNS for an external service may steer token-validation requests to an internal host or a malicious proxy that logs Authorization headers. Because JWT tokens are often passed in the Authorization: Bearer header, interception at the DNS layer can expose bearer tokens even when the application believes it is communicating with a trusted backend. This becomes critical in microservice architectures where Axum services call each other using service names that rely on DNS resolution, and where token validation logic may not enforce strict certificate pinning or mutual TLS.
Jwt Tokens-Specific Remediation in Axum — concrete code fixes
Remediation centers on ensuring that hostname resolution, TLS verification, and JWT validation are performed with strict controls. You should pin public key endpoints or certificates, avoid relying on potentially poisoned DNS for discovery, and enforce robust hostname verification when validating JWTs issued for your domain. Below are concrete Axum examples that demonstrate secure handling of JWT tokens while mitigating risks related to DNS cache poisoning.
1. Use a pinned HTTPS client with strict TLS and hostname verification
Use an HTTP client that enforces certificate and hostname validation rather than relying on system DNS at runtime for critical endpoints.
use reqwest::Client;
use std::sync::Arc;
use tokio::runtime::Runtime;
let cert = reqwest::Certificate::from_pem(include_bytes!("/path/to/cert.pem"))?;
let client = Client::builder()
.add_root_certificate(cert)
.build()?;
// Use client to fetch JWKS from a pinned endpoint
let jwks_uri = "https://auth.example.com/.well-known/jwks.json";
let resp = client.get(jwks_uri).send().await?;
let jwks: jsonwebtoken::jwk::JwkSet = resp.json().await?;
2. Validate issuer and audience claims strictly
When verifying JWTs, explicitly check the issuer (iss) and audience (aud) to ensure tokens are intended for your service and were issued by a trusted authority, reducing reliance on DNS-based discovery for trust.
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation, TokenData};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
iss: String,
aud: String,
exp: usize,
}
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...";
let mut validation = Validation::new(Algorithm::RS256);
validation.validate_exp = true;
validation.set_issuer(&["https://auth.example.com"]);
validation.set_audience(&["my-axum-service"]);
let token_data: TokenData = decode(
token,
&DecodingKey::from_rsa_pem(include_bytes!("/path/to/public_key.pem"))?,
&validation,
)?;
3. Avoid dynamic discovery for critical endpoints without integrity checks
If you must use discovery, pin the discovery document or hash and validate it before use, or rely on a static, trusted configuration to prevent DNS cache poisoning from redirecting token validation flows.
use jsonwebtoken::jwk::JwkSet;
use serde_json::Value;
async fn get_trusted_jwks() -> Result> {
// Prefer static configuration over runtime discovery
let data = std::fs::read_to_string("static_jwks.json")?;
let jwks: JwkSet = serde_json::from_str(&data)?;
Ok(jwks)
}