Data Exposure in Axum with Mutual Tls
Data Exposure in Axum with Mutual Tls
Axum is a popular Rust web framework that, like many modern frameworks, supports mutual TLS (mTLS) to authenticate both client and server. When mTLS is configured, the server requests a client certificate and validates it before processing the request. If this setup is not implemented carefully, data exposure can occur because application-level logic may inadvertently allow sensitive information to be returned even when the mTLS handshake succeeds but the caller is not properly authorized.
In Axum, data exposure with mTLS typically arises from conflating transport-layer authentication (the mTLS handshake) with application-level authorization. The framework can extract client certificate details from the request extensions once the TLS layer has authenticated the peer, but it is the developer’s responsibility to ensure that the extracted identity is checked against business rules before returning any sensitive data. Without explicit authorization checks after certificate validation, an attacker who possesses a valid client certificate can access endpoints or data they should not see, leading to a data exposure finding in categories such as BOLA/IDOR or Property Authorization.
Consider an endpoint that returns user profile information. If the route handler trusts the presence of a client certificate and directly uses the subject or serial from the certificate to form a query, it may expose data belonging to other users with valid certificates:
// WARNING: This example illustrates a potential data exposure issue
use axum::extract::Request;
use axum::response::Json;
use axum::Extension;
use serde::Serialize;
#[derive(Serialize)]
struct UserProfile {
user_id: u64,
username: String,
email: String,
}
async fn get_profile(
Extension(req): Extension,
) -> Json {
// Extract certificate subject from request extensions (set by TLS layer)
let subject = req.extensions()
.get::()
.expect("client certificate subject should be present");
// BAD: Using subject directly to fetch data without proper authorization checks
let profile = fetch_user_profile_by_subject(subject).unwrap_or_default();
Json(profile)
}
In this pattern, the framework does not prevent data exposure because the handler assumes that possessing a valid certificate is sufficient to access any data tied to that certificate’s subject. An attacker who can obtain or guess another user’s certificate could iterate through subjects and read profiles they should not access. This aligns with OWASP API Top 10 A01:2023 broken object level authorization when object-level constraints are missing.
Additionally, data exposure can occur in logging or error messages if certificate details or user identifiers are included without redaction. For example, returning a 400 error that echoes the certificate subject can leak information to an attacker. MiddleBrick’s Data Exposure checks look for such risky patterns in the unauthenticated scan, including whether responses or logs inadvertently surface sensitive attributes derived from mTLS client identities.
To mitigate, you must enforce explicit authorization after extracting identity from the certificate and avoid using raw certificate fields as direct data keys. Combine mTLS with application-level role and ownership checks, and ensure error messages do not disclose certificate or user details.
Mutual Tls-Specific Remediation in Axum
Remediation centers on strict separation between transport authentication and application authorization. After mTLS verifies the client, the handler should map the certificate to a user, then verify permissions before accessing or returning data. Avoid using certificate fields as data identifiers directly, and ensure errors do not leak sensitive information.
Below is a safer Axum pattern that extracts the certificate subject, maps it to a verified user, and enforces ownership before returning data:
use axum::extract::Request;
use axum::response::Json;
use axum::Extension;
use serde::Serialize;
use std::collections::HashMap;
#[derive(Serialize)]
struct UserProfile {
user_id: u64,
username: String,
email: String,
}
// Simulated database mapping from certificate subject to user
fn get_user_by_subject(subject: &str) -> Option {
let db: HashMap<&str, UserProfile> = [
("CN=alice,O=Example", UserProfile { user_id: 1, username: "alice".into(), email: "[email protected]".into() }),
("CN=bob,O=Example", UserProfile { user_id: 2, username: "bob".into(), email: "[email protected]".into() }),
].into_iter().collect();
db.get(subject).cloned()
}
// Simulated authorization check: ensure the requesting user is allowed to view the target user_id
fn can_view_profile(requester: &UserProfile, target_user_id: u64) -> bool {
requester.user_id == target_user_id
}
async fn get_profile(
Extension(req): Extension,
) -> Result, (axum::http::StatusCode, String)> {
let subject = req.extensions()
.get::()
.ok_or_else(|| (axum::http::StatusCode::BAD_REQUEST, "Missing client certificate".into()))?;
let requester = get_user_by_subject(subject)
.ok_or_else(|| (axum::http::StatusCode::FORBIDDEN, "Invalid certificate".into()))?;
// Assume target_user_id is obtained from path parameters in a real route
let target_user_id = requester.user_id; // in practice, extract from route
if !can_view_profile(&requester, target_user_id) {
return Err((axum::http::StatusCode::FORBIDDEN, "Not authorized".into()));
}
let profile = get_user_by_subject(subject)
.ok_or_else(|| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "User not found".into()))?;
Ok(Json(profile))
}
This approach ensures that even if a valid certificate is presented, the handler explicitly checks that the requester is allowed to access the specific resource. It also avoids echoing certificate details in errors, reducing information leakage.
In production, integrate certificate parsing with your identity provider and store mappings securely. Combine this practice with middleware that rejects requests without mTLS and ensure your error handling is generic to prevent data exposure through verbose messages.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |