Jwt Misconfiguration in Axum with Mutual Tls
Jwt Misconfiguration in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
When JWT validation is implemented in an Axum service that also enforces Mutual TLS (mTLS), misconfigurations can undermine both authentication and transport-layer security. mTLS ensures the client presents a valid certificate during the TLS handshake, which Axum can enforce via Rust TLS acceptor configuration. Separately, JWT validation typically occurs later in the request pipeline, often as an extractor or middleware layer that parses and verifies tokens.
The combination exposes risk when developers assume mTLS alone provides sufficient identity assurance and relax JWT best practices. For example, if the JWT validation is performed without verifying the issuer (iss) claim against the mTLS client certificate, an attacker who obtains a valid certificate (e.g., via compromised provisioning or a rogue CA) could present it to the mTLS endpoint and still receive a valid JWT from a less-guarded upstream service. Conversely, if JWT validation is mistakenly placed before mTLS verification in the middleware stack, or if the JWT library does not validate required claims like aud and nbf, tokens issued to other audiences or already expired may be accepted.
Real-world attack patterns mirror OWASP API Top 10 2023 A07 and can intersect with insecure default configurations in Axum extractors. For instance, failing to set verify_exp or not checking scope/roles embedded in the JWT can allow privilege escalation even when mTLS client identities are correctly validated. Similarly, missing require_exp or using weak algorithms (e.g., accepting none or asymmetric keys where only symmetric is expected) can let an attacker forge tokens that pass JWT checks despite mTLS being in place.
middleBrick detects such issues by correlating the OpenAPI/Swagger spec (including securitySchemes for openIdConnect and x-mtls extensions where present) with runtime behavior. For Axum services, this includes verifying that JWT validation middleware is ordered after transport-layer authentication and that claims checks are comprehensive. The LLM/AI Security checks further probe for prompt injection or system prompt leakage that could aid an attacker in crafting tokens or bypassing intended validation logic, though the primary concern remains correct sequencing and strict claim validation.
Concrete risk example: an Axum route using Json<Value> with a JWT extractor and a TLS acceptor configured for client authentication, but without verifying that the certificate subject matches the JWT subject. An attacker presenting a valid mTLS certificate but carrying a stolen JWT for a different audience could gain access if audience validation is omitted. middleBrick flags missing claim checks and improper middleware ordering as high-severity findings, providing remediation guidance to tighten both JWT and mTLS configurations.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
To securely combine JWT validation and mTLS in Axum, enforce strict ordering and claim checks. Place mTLS verification at the transport layer and perform JWT validation afterward in middleware or extractors. Below are concrete, syntactically correct examples for Axum using hyper-rustls and jsonwebtoken.
use axum::Router;
use axum::routing::get;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation, TokenData, Header};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use tokio_rustls::rustls::{self, server::ClientCertVerified, RootCertStore, ServerConfig};
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName};
use tower_http::set_header::SetResponseHeaderLayer;
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
iss: String,
aud: String,
exp: usize,
}
async fn handler() -> &'static str {
"secure"
}
fn make_axum_router(decoding_key: DecodingKey, validation: Validation) -> Router {
Router::new()
.route("/", get(handler))
.layer(axum::middleware::from_fn_with_state(
(decoding_key, validation),
jwt_middleware,
))
}
async fn jwt_middleware(
state: (DecodingKey, Validation),
req: axum::http::Request,
next: axum::middleware::Next,
) -> axum::http::Response {
let auth_header = req.headers().get("authorization")
.and_then(|h| h.to_str().ok())
.filter(|s| s.starts_with("Bearer "))
.map(|s| &s[7..]);
match auth_header {
Some(token) => {
let (decoding_key, validation) = &state;
match decode::(token, decoding_key, &validation) {
Ok(token_data) => {
// Ensure claims match expectations, e.g., audience matches mTLS peer
if token_data.claims.aud != "my-api-audience" {
return axum::http::Response::builder()
.status(401)
.body(axum::body::boxed_body("Invalid audience", 0))
.unwrap();
}
next.run(req).await
}
Err(_) => axum::http::Response::builder()
.status(401)
.body(axum::body::boxed_body("Invalid token", 0))
.unwrap(),
}
}
None => axum::http::Response::builder()
.status(401)
.body(axum::body::boxed_body("Missing token", 0))
.unwrap(),
}
}
async fn build_tls_config() -> rustls::ServerConfig {
let mut root_store = RootCertStore::empty();
// Load trusted CA certificates for client verification
root_store.add(&rustls_pemfile::certs(&mut std::io::Cursor::new(include_bytes!("ca.crt").to_vec())).unwrap()[0])
.expect("Failed to add CA cert");
let certs = rustls_pemfile::certs(&mut std::io::Cursor::new(include_bytes!("server.crt").to_vec())).unwrap();
let key = rustls_pemfile::pkcs8_private_keys(&mut std::io::Cursor::new(include_bytes!("server.key").to_vec())).unwrap();
let mut server_config = ServerConfig::builder()
.with_client_cert_verifier(Arc::new(ClientCertVerifier(root_store)))
.with_single_cert(certs, key[0].clone())
.expect("Invalid certificate or key");
server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
server_config
}
async fn run_server() {
let tls_config = build_tls_config();
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let decoding_key = DecodingKey::from_secret("secret".as_ref());
let validation = Validation::new(Algorithm::HS256);
let app = make_axum_router(decoding_key, validation);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
">
Key remediation points:
- Verify JWT
iss,aud,exp, andnbfclaims explicitly; do not rely on mTLS alone for identity. - Ensure JWT validation middleware runs after transport-layer mTLS verification to avoid accepting unauthenticated requests.
- Use strong algorithms and avoid
noneor deprecated methods; prefer asymmetric keys where feasible. - Map JWT audience and scopes to mTLS client certificate attributes to enforce least privilege.
middleBrick’s scans will highlight missing claim checks, incorrect middleware ordering, and weak algorithms, aligning findings with OWASP API Top 10 and common compliance mappings to guide remediation.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |