HIGH time of check time of useaxummutual tls

Time Of Check Time Of Use in Axum with Mutual Tls

Time Of Check Time Of Use in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability

Time Of Check Time Of Use (TOCTOU) is a class of race condition where a decision is made based on the state of a resource at one moment, but the actual use of that resource occurs later after the state may have changed. In Axum, when Mutual TLS is used for client authentication, a common pattern is to inspect the client certificate (e.g., subject, SAN, or extended key usage) during the request’s authorization phase and then rely on that same certificate later in business logic or data access without re-verifying intent or context.

With Mutual TLS, the TLS handshake ensures the client possesses a valid private key matching the presented certificate, but it does not guarantee that the certificate’s attributes remain authoritative for the specific operation being performed. If Axum handlers cache or assume certificate-derived claims (such as user ID or role) between the TLS verification and the actual use, an attacker who can manipulate runtime state (e.g., via concurrent requests, mutable in-memory state, or delayed asynchronous workflows) may exploit the window between check and use. For example, a handler might read the certificate’s common name at route guard time to authorize a request to /users/{id}, then later construct a database query using that cached name without confirming the target user ID matches the authenticated identity. Because the check and use are separated, a malicious client with a valid certificate but unauthorized identity could potentially influence the runtime path or data bindings between them.

In Axum, this often surfaces when developers combine tower-http middleware for TLS with custom authorization logic that does not re-validate critical assertions at the point of use. The framework does not inherently serialize or isolate the check from the use, so if your application code introduces a gap—such as storing certificate-derived claims in a request extension, using async move closures that capture state, or performing additional logic based on cached values—TOCTOU can be realized. This is especially relevant when integrating with downstream systems whose permissions may differ from the certificate assertions, or when the handler performs multiple steps that assume a stable identity across awaits or futures.

Mutual Tls-Specific Remediation in Axum — concrete code fixes

To mitigate TOCTOU in Axum with Mutual TLS, ensure that any authorization decision tied to certificate attributes is re-evaluated at the moment of use, and avoid caching sensitive claims beyond the immediate handler scope. Prefer deriving and validating per-request data inside the handler rather than sharing state across awaits or extensions. Below are concrete patterns and code examples.

1. Re-validate within the handler: Do not reuse certificate-derived claims from middleware or extensions for later decisions. Instead, extract the certificate information directly within the business logic or service call, ensuring the check and use happen atomically from the handler’s perspective.

use axum::extract::State;
use axum::routing::get;
use axum::Router;
use std::net::SocketAddr;
use tokio_rustls::rustls::ServerCertVerifier;
use tower_http::set_header::SetResponseHeaderLayer;
use tower_http::trace::TraceLayer;

// A shared app state that does NOT include per-request auth state
struct AppState;

async fn user_handler(
    State(state): State<AppState>,
    // Assume mutual TLS is enforced upstream by tower-http or hyper with client cert verification
    // Extract certificate info inside the handler to avoid stale claims
    // In practice, you may use a tower-http::OriginalUri or custom extractor that re-verifies
) -> Result<String, (StatusCode, String)> {
    // Perform authorization here, e.g., call a service that re-checks certificate attributes
    // or maps the request to a user identity via a secure, idempotent lookup keyed by the TLS session.
    Ok("OK".to_string())
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/user", get(user_handler))
        .with_state(AppState);

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

2. Use one-shot extractors and avoid storing certificate claims in request extensions: Extensions are mutable and shared across middleware and handlers; storing sensitive identity assertions there can create a stale check between middleware authorization and handler use. Instead, use a dedicated extractor that performs necessary validation at handler invocation time, or pass minimal, verified identifiers only.

3. Ensure idempotent authorization functions: If you must call an authorization service or perform additional checks, design those functions to be idempotent and to re-fetch permissions or mappings based on the live TLS session context rather than cached values. Combine this with strict concurrency controls in your application logic to reduce race windows.

These approaches reduce the time window between authorization check and use by keeping validation close to the point of use and avoiding shared mutable state that can become inconsistent across asynchronous boundaries.

Frequently Asked Questions

Can middleware that caches certificate claims cause TOCTOU in Axum?
Yes. If middleware caches certificate-derived attributes (such as user ID or roles) in request extensions or shared state, and later handlers use those cached values without re-verifying, the check and use can be separated by asynchronous execution or concurrent requests, creating a TOCTOU window.
Does using tower-http’s tls_require_auth eliminate TOCTOU risks?
No. tower-http can enforce that a client certificate is presented and valid during the TLS handshake, but it does not prevent application-level logic from introducing race conditions between authorization checks and use. You must ensure handler logic re-validates critical assertions at the point of use.