Pii Leakage in Axum
How Pii Leakage Manifests in Axum
PII leakage in Axum applications typically occurs through improper data handling in request handlers and middleware. The most common pattern involves extracting user data from requests without proper filtering before sending responses or logging. For example, an Axum endpoint might directly serialize a user struct containing sensitive fields:
use axum::extract::Json;
use serde::Serialize;
#[derive(Serialize)]
struct User {
id: i32,
name: String,
email: String, // PII
ssn: String, // Highly sensitive PII
address: String, // PII
}
async fn get_user() -> Json {
let user = User {
id: 1,
name: "John Doe".into(),
email: "john@example.com".into(),
ssn: "123-45-6789".into(),
address: "123 Main St".into(),
};
Json(user) // Exposes all fields including PII
}
Another Axum-specific manifestation occurs in error responses. When using extractors like Json or Query, Axum's error handling can inadvertently expose raw request data containing PII:
async fn create_user(Json(payload): Json) -> Result, Problem> {
// If validation fails, Axum's default error handling might include raw payload
// in the error response, exposing PII
let user = create_user_in_db(payload).await?;
Ok(Json(user))
}
Middleware logging presents another risk. Axum's middleware chain can log entire request bodies or headers without filtering:
use axum::middleware::Next;
use axum::http::Request;
async fn logging_middleware(mut req: Request, next: Next) -> Result {
let body = req.body_mut().clone(); // Captures entire request body
log::info!("Request: {:?}", body); // Logs PII without filtering
let res = next.run(req).await;
res
}
Query parameter extraction is particularly dangerous for PII leakage:
async fn search_users(
Query(params): Query,
) -> Json> {
// SearchParams might contain PII in fields like 'email' or 'ssn'
let results = search_db(¶ms).await?;
Json(results) // Returns results that might contain the original PII query
}
#[derive(Deserialize)]
struct SearchParams {
email: Option, // PII in query
ssn: Option, // Highly sensitive PII
}
Axum-Specific Detection
Detecting PII leakage in Axum applications requires examining both the code structure and runtime behavior. The primary detection method involves analyzing how data flows through extractors and response handlers. middleBrick's black-box scanning approach is particularly effective for Axum APIs since it tests the actual endpoints without requiring source code access.
Key detection patterns for Axum-specific PII leakage:
- Response serialization of structs with sensitive fields - middleBrick identifies endpoints that return entire structs without field filtering
- Query parameter extraction - scanning tests endpoints with PII-like values in query strings to see if they're reflected in responses
- JSON body extraction - testing POST endpoints with PII payloads to verify they're not logged or returned
- Path parameter usage - checking if user identifiers in paths lead to PII exposure
Using middleBrick's CLI to scan an Axum API:
npx middlebrick scan https://api.example.com/users
The scanner tests 12 security categories including Property Authorization (checking if authenticated users can access others' data) and Data Exposure (verifying PII isn't returned in responses). For Axum applications, middleBrick specifically looks for:
- Structs derived with Serialize that contain PII fields being returned directly
- Extractors that capture entire request bodies without validation
- Middleware that logs request data without filtering
middleBrick's OpenAPI analysis is particularly useful for Axum apps using tower-web or similar frameworks that generate OpenAPI specs. The scanner cross-references spec definitions with actual endpoint behavior to identify mismatches between documented and actual data exposure.
Manual code review detection for Axum PII leakage should focus on:
// Red flags in Axum code
#[derive(Serialize)] // Check if this struct contains PII fields
struct UserProfile {
id: i32,
email: String, // PII
phone: String, // PII
ssn: String, // Highly sensitive PII
}
async fn get_profile(user_id: u32) -> Json {
let profile = get_user_profile(user_id).await?;
Json(profile) // Direct serialization exposes all fields
}
Axum-Specific Remediation
Remediating PII leakage in Axum requires a multi-layered approach using Axum's native features and Rust's type system. The most effective strategy is implementing explicit data filtering before serialization.
DTO (Data Transfer Object) pattern for Axum:
use axum::extract::Json;
use serde::Serialize;
#[derive(Serialize)]
struct UserProfileResponse {
id: i32,
name: String,
// Explicitly exclude PII fields
}
#[derive(Deserialize)]
struct UserProfile {
id: i32,
name: String,
email: String, // PII - not in response
phone: String, // PII - not in response
ssn: String, // Highly sensitive PII - not in response
}
async fn get_user_profile(
user_id: u32,
Json(_): Json<()>, // For POST endpoints
) -> Json {
let user = get_user_from_db(user_id).await?;
let response = UserProfileResponse {
id: user.id,
name: user.name,
};
Json(response) // Only safe fields returned
}
Middleware filtering for PII in logs:
use axum::middleware::Next;
use axum::http::{Request, Response};
use axum::http::header::HeaderName;
async fn pii_filtering_middleware(mut req: Request, next: Next) -> Result {
// Remove sensitive headers
let sensitive_headers = [
"authorization",
"cookie",
"x-api-key",
];
for header in sensitive_headers {
if let Ok(name) = HeaderName::from_bytes(header.as_bytes()) {
req.headers_mut().remove(name);
}
}
// Filter PII from request body (for JSON requests)
let filtered_req = req.map_body(|_mime, body| {
// In production, implement actual PII filtering logic
// This is a placeholder for demonstration
body
});
let res = next.run(filtered_req).await;
res
}
Using Axum's response rejection for PII validation:
use axum::{routing, Router};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::extract::Path;
async fn get_user_sensitive_data(
Path(user_id): Path,
) -> impl IntoResponse {
// Validate user has permission to access this data
if !has_access_permission(user_id).await {
return (StatusCode::FORBIDDEN, "Access denied");
}
let data = get_sensitive_data(user_id).await?;
// Filter PII before returning
let filtered_data = filter_pii(&data);
axum::Json(filtered_data)
}
fn filter_pii(data: &UserData) -> FilteredData {
FilteredData {
id: data.id,
name: data.name.clone(),
// Explicitly exclude PII fields
}
}
Query parameter validation in Axum:
use axum::extract::Query;
use serde::Deserialize;
#[derive(Deserialize)]
struct SearchQuery {
query: String,
page: Option,
// Avoid PII in query parameters when possible
}
async fn search_users(
Query(params): Query,
) -> Json> {
// Validate query doesn't contain PII
if contains_pii(¶ms.query) {
return Json(vec![]); // Or return error
}
let results = search_users_in_db(¶ms).await?;
let filtered_results: Vec = results
.into_iter()
.map(|user| UserProfileResponse {
id: user.id,
name: user.name,
})
.collect();
Json(filtered_results)
}
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 |