Token Leakage in Axum with Mongodb
Token Leakage in Axum with Mongodb — how this specific combination creates or exposes the vulnerability
Token leakage in an Axum service that uses MongoDB as a backend typically occurs when authentication tokens, session identifiers, or API keys are inadvertently exposed through logging, error messages, or insecure data handling in the data access layer. Axum, a web framework for Rust, does not leak tokens by itself, but application code that mishandles token storage, serialization, or MongoDB queries can create exposure paths.
One common pattern is storing user tokens directly in MongoDB documents, such as in a users or sessions collection, and then inadvertently including those fields in responses or logs. For example, a developer might serialize an entire MongoDB document retrieved via the MongoDB Rust driver and return it from an Axum handler without filtering sensitive fields. This can expose tokens in API responses or browser-side JavaScript if the endpoint is not properly protected.
Another vector arises from improper error handling. If an Axum handler performs a MongoDB operation (e.g., a find_one or update_one) and the driver returns an error that includes query details or stack traces, tokens embedded in query filters or logs may be leaked through those error messages. For instance, a search query like doc! { "api_key": key } could expose the key in a verbose driver error if the key is mistakenly included in debug output.
Additionally, logging practices in Axum middleware can inadvertently capture tokens. If request logging captures headers or body fields and one of those fields contains an Authorization header with a bearer token, and that log is stored in a system that also writes to MongoDB (for audit trails), the token becomes persisted in the database. This creates a long-term exposure risk, especially if the MongoDB collection containing logs is not properly restricted.
These risks are compounded when MongoDB change streams or notifications are used within Axum to react to database events. If a change stream pipeline emits events containing token-like fields, and those events are forwarded to external systems or logged without redaction, tokens can be leaked across service boundaries. The combination of Axum's flexibility in handling requests and MongoDB's document-oriented nature increases the surface area for accidental exposure if secure coding practices are not enforced consistently.
Mongodb-Specific Remediation in Axum — concrete code fixes
To prevent token leakage in an Axum application using MongoDB, follow targeted remediation steps that address storage, querying, logging, and response serialization. Below are concrete code examples demonstrating secure patterns.
1. Exclude token fields from API responses
Use projection to return only necessary fields from MongoDB queries, and explicitly strip sensitive fields before serialization.
use mongodb::{bson::{doc, Document}, options::FindOptions};
use axum::extract::State;
struct AppState {
db: mongodb::Database,
}
async fn get_user_profile(
State(state): State<AppState>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let users = state.db.collection("users");
// Only fetch fields needed for the response
let filter = doc! { "user_id": "123" };
let options = FindOptions::builder().projection(doc! { "username": 1, "email": 1, "_id": 0 }).build();
let result = users.find_one(filter, options).await.map_err(|e| {
error::ErrorInternalServerError("Database error")
})?;
if let Some(user) = result {
// Ensure token fields are not present
let json = serde_json::to_string(&user).map_err(|e| {
error::ErrorInternalServerError("Serialization error")
})?;
Ok(Json(serde_json::from_str(&json).unwrap()))
} else {
Err((StatusCode::NOT_FOUND, "User not found".to_string()))
}
}
2. Secure token storage with hashed references
Instead of storing raw tokens in MongoDB, store references or hashed values and keep the actual tokens in a secure runtime context.
use mongodb::bson::{doc, oid::ObjectId, Document};
async fn store_session(
db: &mongodb::Database,
user_id: &str,
token_hash: &str,
) -> Result<(), mongodb::error::Error> {
let sessions = db.collection("sessions");
let doc = doc! {
"user_id": user_id,
"token_hash": token_hash,
"created_at": chrono::Utc::now(),
};
sessions.insert_one(doc, None).await?;
Ok(())
}
3. Sanitize error messages
Avoid propagating raw MongoDB driver errors to clients and ensure logs do not contain tokens.
use tracing::error;
async fn safe_update_token(
db: &mongodb::Database,
user_id: &str,
new_token_hash: &str,
) -> Result<(), &'static str> {
let users = db.collection("users");
let filter = doc! { "user_id": user_id };
let update = doc! { "$set": { "token_hash": new_token_hash } };
match users.update_one(filter, update, None).await {
Ok(_) => Ok(()),
Err(e) => {
// Log without exposing token values
error!(user_id = %user_id, "Failed to update token");
Err("Internal server error")
}
}
}
4. Redact sensitive fields in logs and middleware
Implement Axum middleware that scrubs sensitive headers before logging.
use axum::extract::Request;
use axum::middleware::Next;
use std::future::Future;
pub async fn redaction_middleware(
request: Request,
next: Next<F>,
) -> R::Output
where
F: FnOnce() -> R,
R: Future<Output = axum::response::Response> + Send + 'static,
{
// Remove or mask Authorization header before logging
let request = if request.headers().contains_key("authorization") {
let mut req = request;
req.headers_mut().remove("authorization");
req
} else {
request
};
next.run(request).await
}