Timing Attack in Axum with Jwt Tokens
Timing Attack in Axum with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A timing attack in the context of Axum applications that validate JWT tokens occurs when the token verification step does not execute in constant time. If the comparison between the computed signature and the signature present in the JWT is performed with a short-circuit string comparison, an attacker can measure response times to infer information about the expected signature or key material. In Axum, this typically surfaces during the JWT validation stage where a middleware or extractor decodes and verifies the token before allowing access to protected routes. The presence of JWT tokens does not inherently introduce a vulnerability, but the implementation choices in how tokens are verified can create a measurable timing side channel.
Attackers can send valid and invalid tokens and observe small differences in the time taken to receive a 401 Unauthorized response. If the verification logic first checks a token identifier or issuer claim before performing a cryptographic comparison, or if it uses conditional logic that exits early upon a mismatch, the server may respond faster for certain invalid tokens than for others. Over many requests, these small differences can be amplified to recover partial information about a signing key or to distinguish between a malformed token and a valid token with an incorrect signature. This is particularly relevant when the JWT tokens are used for session management or authorization in sensitive endpoints exposed by the Axum service.
The risk is compounded when the Axum application is deployed in environments where network latency is low and an attacker has reliable, low-latency access to the API endpoint, such as within the same data center or through a compromised network path. Because Axum is a Rust web framework, developers may assume that low-level code is immune to timing leaks; however, unsafe blocks, unchecked conversions, or non-constant-time cryptographic operations can still introduce variability. MiddleBrick’s scans detect such timing-related anomalies by analyzing the unauthenticated attack surface and flagging inconsistent response patterns across token validation outcomes, correlating findings with the broader category of Authentication and BOLA/IDOR checks.
Jwt Tokens-Specific Remediation in Axum — concrete code fixes
To mitigate timing attacks involving JWT tokens in Axum, ensure that all cryptographic comparisons are performed in constant time and that the validation flow does not branch on sensitive data before the comparison completes. Instead of manually parsing headers and payloads and performing early validation checks, rely on established JWT libraries that are designed to execute verification in a time-invariant manner. In Rust, the jsonwebtoken crate provides decoding and validation routines that avoid leaking timing information when used correctly.
Below is an example of insecure Axum extractor logic that can lead to timing discrepancies. This code validates the token but performs an early check on the issuer claim before verifying the signature, which can expose timing differences.
use axum::{{
async_trait,
extract::{FromRequest, Request},
http::StatusCode,
response::IntoResponse,
}};
use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
iss: String,
}
struct JwtExtractor {
token: String,
}
#[async_trait]
impl FromRequest for JwtExtractor {
type Rejection = (StatusCode, &'static str);
async fn from_request(req: Request) -> Result {
let token = req.headers()
.get("authorization")
.and_then(|v| v.to_str().ok())
.map(|s| s.trim_start_matches("Bearer ").to_string())
.ok_or((StatusCode::UNAUTHORIZED, "missing token"))?;
// Insecure: early issuer check before constant-time verification
let header = decode_header(&token).map_err(|_| (StatusCode::UNAUTHORIZED, "invalid header"))?;
if header.alg != Algorithm::HS256 {
return Err((StatusCode::UNAUTHORIZED, "unsupported algorithm"));
}
let validation = Validation::new(Algorithm::HS256);
let token_data = decode::(
&token,
&DecodingKey::from_secret("my-secret-key".as_ref()),
&validation,
).map_err(|_| (StatusCode::UNAUTHORIZED, "invalid token"))?;
if token_data.claims.iss != "expected-issuer" {
return Err((StatusCode::UNAUTHORIZED, "invalid issuer"));
}
Ok(JwtExtractor { token })
}
}
The secure approach moves the issuer check after decoding and uses the library’s validation flow, which performs cryptographic operations in a way that does not branch on secret data. The following code demonstrates a safer pattern where the entire validation is handled by decode with a strict validation configuration, avoiding early exits based on intermediate claims.
use axum::{
async_trait,
extract::{FromRequest, Request},
http::StatusCode,
};
use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
iss: String,
}
struct SecureJwtExtractor {
claims: Claims,
}
#[async_trait]
impl FromRequest for SecureJwtExtractor {
type Rejection = (StatusCode, &'static str);
async fn from_request(req: Request) -> Result {
let token = req.headers()
.get("authorization")
.and_then(|v| v.to_str().ok())
.map(|s| s.trim_start_matches("Bearer ").to_string())
.ok_or((StatusCode::UNAUTHORIZED, "missing token"))?;
let validation = Validation::new(Algorithm::HS256);
let token_data = decode::(
&token,
&DecodingKey::from_secret("my-secret-key".as_ref()),
&validation,
).map_err(|_| (StatusCode::UNAUTHORIZED, "invalid token"))?;
// After verification, inspect claims as needed
if token_data.claims.iss != "expected-issuer" {
return Err((StatusCode::UNAUTHORIZED, "invalid issuer"));
}
Ok(SecureJwtExtractor { claims: token_data.claims })
}
}
Additionally, ensure that the secret used for signing is of sufficient entropy and that the same algorithm is enforced consistently across services. For production Axum services, consider integrating MiddleBrick’s scans to verify that JWT validation paths do not introduce timing variability and align with OWASP API Top 10 and related compliance mappings.