Session Fixation in Axum with Mutual Tls
Session Fixation in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
Session fixation occurs when an application accepts a session identifier provided by the client without verifying its origin or ensuring it is newly issued after authentication. In Axum, this typically manifests when a session token (e.g., a JWT or session cookie) is accepted from an untrusted source, such as a URL query parameter or a header, and the server uses that value without regeneration upon privilege elevation.
Mutual TLS (mTLS) adds client certificate authentication at the transport layer, ensuring that only authorized clients can initiate TLS connections. While mTLS strongly authenticates the client, it does not inherently protect the application layer session lifecycle. If an Axum service relies on mTLS for identity but then accepts session tokens from the client over the authenticated TLS channel without reissuing them, an attacker can fixate a session by persuading a victim to use a known token before authentication. After the victim authenticates with a client certificate, the server trusts the TLS identity but continues to use the attacker-provided session token, effectively binding the authenticated identity to the attacker’s token.
For example, consider an Axum API that terminates mTLS at the proxy or via Rustls and then reads a session_id from a cookie or header. The flow might look like:
- An attacker provides a link with ?session_id=attacker123 to a victim.
- The victim, possessing a valid client certificate, authenticates successfully via mTLS.
- The server validates the certificate, associates the request with the client’s certificate identity, and then uses session_id=attacker123 for the session.
Because the server trusts the mTLS identity but does not rotate the session token post-authentication, the attacker can later use attacker123 to impersonate the victim. This is a classic session fixation vector that exploits the gap between transport-layer authentication and application-layer session management. The risk is elevated when developers assume mTLS alone suffices for session integrity without enforcing token regeneration after authentication.
Additionally, if the Axum application exposes an endpoint that accepts a session token as a query parameter for convenience (e.g., for debugging or callback URLs), mTLS does not mitigate the risk of that token being captured or manipulated. The combination of mTLS and session fixation illustrates why transport security must be complemented with secure session lifecycle practices, including token regeneration and strict source validation.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
To mitigate session fixation in Axum when using mTLS, ensure that session identifiers are regenerated after successful authentication and are never directly derived from or accepted from the client. Below are concrete, syntactically correct Axum examples using Rustls mTLS.
First, configure an Axum server with Rustls mTLS using a root certificate to verify client certificates:
use axum::Router;
use std::net::SocketAddr;
use tokio_rustls::rustls::{ServerConfig, Certificate, PrivateKey};
use tokio_rustls::TlsAcceptor;
use std::sync::Arc;
async fn build_tls_config() -> Arc<TlsAcceptor> {
let mut server_config = ServerConfig::builder()
.with_safe_defaults()
.with_client_auth_verifier(Arc::new(|certs| {
// Verify client certs against a trusted CA
// Return Ok(()) if valid, Err otherwise
Ok(())
}))
.with_single_cert(
vec![Certificate(vec![/* server cert der */])],
PrivateKey(vec![/* server key der */]),
)
.unwrap();
Arc::new(TlsAcceptor::from(Arc::new(server_config)))
}
#[tokio::main]
async fn main() {
let tls_acceptor = build_tls_config().await;
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
// bind and serve with TLS
}
In your Axum routes, after verifying mTLS identity (e.g., via peer certificate details), regenerate the session token:
use axum::{routing::get, Json, Extension};
use uuid::Uuid;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Session {
user_id: String,
session_token: String,
}
async fn login_handler(
Extension(state): Extension<AppState>,
// Assume we validated client cert and extracted identity
) -> Json<Session> {
let new_token = Uuid::new_v4().to_string(); // regenerate token
let session = Session {
user_id: "user-from-cert".to_string(),
session_token: new_token,
};
// Set session in secure, http-only cookie or return token in body
Json(session)
}
Ensure that any incoming session token from cookies or headers is ignored for authorization after mTLS authentication. Instead, derive identity from the validated certificate and issue a fresh token. Avoid accepting session_id as a query parameter or header unless strictly necessary, and if required, treat it as an opaque value that is never used to establish privilege without revalidation.
For production, combine this with secure cookie attributes (HttpOnly, Secure, SameSite) and consider using the middleBrick CLI to scan your Axum endpoints for session fixation and other OWASP API Top 10 findings:
middlebrick scan https://api.example.com
The CLI provides a per-category breakdown and prioritized findings with remediation guidance, helping you detect insecure session handling patterns early.