Type Confusion in Axum with Mutual Tls
Type Confusion in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
Type confusion in an Axum service that uses mutual TLS (mTLS) arises when runtime representations of data do not match the expected types encoded in the application logic, and mTLS is used to establish client identity without correcting these mismatches. Axum is a web framework built on typed Rust handlers; when deserialization, path extraction, or header parsing produces values that are later interpreted as a different variant or structure, the mismatch can lead to unexpected behavior. Enabling mTLS adds a layer of client verification via TLS certificates, but it does not inherently validate or constrain the shape of application-level data. If the handler assumes a strongly typed structure derived from a deserialized JSON body or from path parameters, but an attacker provides values that cause the deserializer to map fields to incorrect Rust types, the runtime may treat an integer as a string, an enum as a struct, or a different variant of a tagged union as another. Because mTLS verifies the client’s certificate, the server may place additional trust in the authenticated client’s requests and process them with elevated privileges or fewer checks, inadvertently expanding the impact of the type confusion.
Consider an endpoint that expects an enum to distinguish between two resource operations, where mTLS identifies an admin client. A crafted payload that supplies a value causing the deserializer to pick the wrong enum variant can lead the server to perform an action intended for a higher-privilege context. The combination of mTLS assurance and unchecked type interpretation means the server may bypass intended authorization checks because it trusts the authenticated identity while still operating on malformed data. In OpenAPI/Swagger analysis, such issues may surface as missing constraints on numeric ranges or absent discriminators for oneOf/anyOf schemas; runtime findings can reveal that responses contain data derived from misinterpreted types. This illustrates why type-safety practices must be paired with transport-layer identity checks rather than relying on mTLS alone to enforce correct data handling.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
Remediation focuses on strict validation of deserialized and extracted data, independent of mTLS client identity. Use strong Rust types, explicit discriminants, and schema validation so that type confusion cannot occur even when mTLS confirms a client’s certificate.
use axum::{
extract::State,
routing::post,
Router,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use axum::http::HeaderValue;
use axum::extract::ConnectInfo;
use axum_extra::headers::authorization::Bearer;
use axum_extra::headers::Authorization;
use axum_extra::extract::SecureHeadersLayer;
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
use std::sync::Arc;
use axum::async_trait;
use axum::extract::FromRequestParts;
use axum::http::{request::Parts, StatusCode};
use axum::response::IntoResponse;
#[derive(Debug, Deserialize, Serialize)]
struct OperationRequest {
#[serde(rename = "type")]
operation_type: OperationType,
#[serde(flatten)]
details: OperationDetails,
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
enum OperationType {
Read,
Write,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
enum OperationDetails {
Read { resource_id: u64 },
Write { resource_id: u64, payload: String },
}
async fn handle_operation(
State(cfg): State>,
ConnectInfo(addr): ConnectInfo,
Authorization(token): Authorization,
body: String,
) -> impl IntoResponse {
// Validate mTLS-derived identity via certificate fields if needed
let principal = cfg.validate_client(&addr);
// Strict deserialization with serde; mismatched types will fail
let req: OperationRequest = match serde_json::from_str(&body) {
Ok(r) => r,
Err(e) => return (StatusCode::BAD_REQUEST, format!("invalid payload: {e}")).into_response(),
};
match req.operation_type {
OperationType::Read => {
// handle read with explicit enum matching
if let OperationDetails::Read { resource_id } = req.details {
// business logic
}
}
OperationType::Write => {
if let OperationDetails::Write { resource_id, payload } = req.details {
// business logic
}
}
}
"ok"
}
struct Config {
ssl_acceptor: axum_server::SslAcceptorConfig,
}
impl Config {
fn validate_client(&self, addr: &SocketAddr) -> String {
// Placeholder for mTLS identity checks derived from certificate fields
addr.ip().to_string()
}
}
#[tokio::main]
async fn main() {
let mut ssl = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
ssl.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
ssl.set_certificate_chain_file("cert.pem").unwrap();
let ssl_config = axum_server::SslAcceptorConfig::new(ssl);
let config = Arc::new(Config { ssl_acceptor: ssl_config });
let app = Router::new()
.route("/operation", post(handle_operation))
.with_state(config);
let listener = tokio::net::TcpListener::bind("0.0.0.0:8443").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
The example uses serde’s strict deserialization and explicit enum matching to avoid type confusion. Even with mTLS providing client authentication, the handler does not assume elevated trust in the payload’s structure. For additional safety, integrate schema validation libraries (such as validator or custom checks) on fields like resource_id to enforce ranges and types. In OpenAPI/Swagger, ensure the oneOf/anyOf definitions and required discriminators are present so code generation produces precise types; this aligns scan findings from middleBrick’s OpenAPI/Swagger analysis with runtime behavior.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |