Excessive Data Exposure in Axum
How Excessive Data Exposure Manifests in Axum
Excessive Data Exposure in Axum applications typically occurs when developers inadvertently expose sensitive data through API endpoints. In Axum, this often manifests through several specific patterns:
use axum::extract::{Json, State};
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
struct User {
id: i32,
username: String,
email: String,
password_hash: String, // Sensitive data exposed!
ssn: String, // SSN exposed!
credit_card_last_four: String,
}
async fn get_user(Json(params): Json<UserIdParams>) -> Json<User> {
// Database query returns entire User struct
let user = db::get_user_by_id(params.id).await;
Json(user) // Serializes ALL fields including sensitive data
}
The above Axum handler demonstrates a common vulnerability where the entire User struct is serialized and returned to the client. Even if the database query is secure, the serialization process exposes all struct fields.
Another Axum-specific manifestation occurs with extractor patterns:
async fn get_user_details(
State<AppState> state: State<AppState>,
Path<(i32, String)> path: Path<(i32, String)>
) -> Json<User> {
let (user_id, _) = path.0;
let user = db::get_user_with_sensitive_data(user_id).await;
// Axum automatically serializes the entire struct
Json(user) // Exposes all fields including PII
}
Database integration patterns in Axum also contribute to this issue. When using libraries like sqlx or tokio-postgres with Axum, developers often map database rows directly to structs without considering data exposure:
async fn list_users(
Query<ListParams> params: Query<ListParams>,
State<DbPool> db: State<DbPool>
) -> Json<Vec<User>> {
let rows = sqlx::query_as!(
User,
"SELECT * FROM users WHERE active = $1",
true
)
.fetch_all(db)
.await;
Json(rows) // Returns ALL user columns including sensitive data
}
The problem is compounded when Axum's automatic JSON serialization works against security. The framework's convenience becomes a liability when sensitive fields are included in response structs without explicit filtering.
Axum-Specific Detection
Detecting Excessive Data Exposure in Axum applications requires both static analysis and runtime scanning. Here are Axum-specific detection approaches:
Static Analysis with Rust Analyzer
use axum::extract::Json;
use serde::Serialize;
// Identify structs that are directly returned in handlers
#[derive(Serialize)]
struct SensitiveUser {
id: i32,
#[serde(skip_serializing)] // This should be used more often
password_hash: String,
#[serde(skip_serializing)]
ssn: String,
email: String,
username: String,
}
// Detection pattern: look for structs with sensitive fields
// that lack serde(skip_serializing) attributes
middleBrick API Security Scanner can detect Excessive Data Exposure in Axum applications by:
- Analyzing OpenAPI specs generated from Axum routes
- Scanning actual API responses to identify exposed sensitive data
- Testing for BOLA (Broken Object Level Authorization) vulnerabilities that often accompany data exposure
Runtime Testing with Axum
use axum::{routing::get, Router};
use tower_http::trace::TraceLayer;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/api/users/:id", get(get_user))
.layer(TraceLayer::new_for_http());
// Use middleware to log and analyze responses
axum::Server::bind(&([127, 0, 0, 1], 3000))
.serve(app.into_make_service())
.await
.unwrap();
}
// Test with curl or automated tools
// curl http://localhost:3000/api/users/1 | jq '.password_hash, .ssn'
middleBrick CLI for Axum APIs
# Install middleBrick CLI
npm install -g middlebrick
# Scan your Axum API endpoint
middlebrick scan http://localhost:3000/api/users/1
# The scanner will:
# 1. Analyze the OpenAPI spec if available
# 2. Send test requests to identify exposed data
# 3. Check for common PII patterns in responses
# 4. Provide a security score with remediation guidance
GitHub Action Integration
name: API Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick Scan
run: |
npm install -g middlebrick
middlebrick scan https://your-axum-api.com/api/users
continue-on-error: true
Axum-Specific Remediation
Remediating Excessive Data Exposure in Axum requires a multi-layered approach. Here are Axum-specific solutions:
Data Transfer Objects (DTOs) Pattern
use serde::{Deserialize, Serialize};
// Database model (contains all fields)
#[derive(sqlx::FromRow)]
struct UserDb {
id: i32,
username: String,
email: String,
password_hash: String,
ssn: String,
credit_card_last_four: String,
}
// API response model (filtered)
#[derive(Serialize)]
struct UserResponse {
id: i32,
username: String,
email: String,
}
async fn get_user(
Path(user_id): Path<i32>,
State<DbPool> db: State<DbPool>
) -> Result<Json<UserResponse>, axum::http::StatusCode> {
let user = sqlx::query_as!(
UserDb,
"SELECT id, username, email FROM users WHERE id = $1",
user_id
)
.fetch_one(db)
.await
.map_err(|_| axum::http::StatusCode::NOT_FOUND)?;
// Only return safe fields
let response = UserResponse {
id: user.id,
username: user.username,
email: user.email,
};
Ok(Json(response))
}
Serde Skip Serialization
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
struct User {
id: i32,
username: String,
email: String,
#[serde(skip_serializing)]
password_hash: String,
#[serde(skip_serializing)]
ssn: String,
#[serde(skip_serializing)]
credit_card_last_four: String,
}
async fn get_user_details(
Json(params): Json<UserIdParams>
) -> Json<User> {
let user = db::get_user_by_id(params.id).await;
Json(user) // Sensitive fields automatically skipped
}
Axum Middleware for Response Filtering
use axum::middleware::Next;
use axum::response::IntoResponse;
use serde_json::Value;
async fn filter_sensitive_data(
mut req: axum::http::Request,
next: Next,
) -> axum::http::Response {
let res = next.run(req).await;
// Check if response is JSON
if let Ok(body) = res.body() {
if let Some(bytes) = body.as_bytes() {
if let Ok(json) = serde_json::from_slice::(bytes) {
// Filter sensitive fields
let filtered = filter_json(json);
let new_body = axum::body::Full(
serde_json::to_vec(&filtered).unwrap()
);
return res.map_body(|_| new_body);
}
}
}
res
}
fn filter_json(value: Value) -> Value {
match value {
Value::Object(mut map) => {
// Remove common sensitive fields
for key in vec!["password", "ssn", "credit_card", "token"] {
map.remove(key);
}
Value::Object(map)
}
_ => value,
}
}
Axum Router Configuration
use axum::Router;
use tower_http::set_header::SetResponseHeaderLayer;
let app = Router::new()
.route("/api/users/:id", get(get_user))
.layer(SetResponseHeaderLayer::if_method(
"content-type",
"application/json; charset=utf-8",
)))
.layer(axum::middleware::from_fn(filter_sensitive_data));
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |