Nosql Injection in Axum with Basic Auth
Nosql Injection in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability
In Axum, a Rust web framework, Nosql Injection occurs when user-controlled input is directly interpolated into a NoSQL query or builder without validation or parameterization. Axum does not provide built-in query sanitization for database drivers; it relies on developers to construct safe queries. When Basic Auth is used, the username and password are typically parsed from the Authorization header and often passed into downstream logic such as database lookups for user authentication or role retrieval.
If the username or password derived from Basic Auth is used to build a NoSQL query—such as a MongoDB filter or a DynamoDB condition—and concatenated as strings, an attacker can inject crafted payloads like {'$ne': null} or {'$where': 'true'} to bypass authentication or extract data. For example, a developer might write a filter using the username directly: doc!{"username": username, "password": password}. If username is admin and password is "$ne: " + "null", the resulting document may unintentionally change semantics, leading to authentication bypass or unauthorized data access.
Since Basic Auth transmits credentials in a base64-encoded header (not encrypted), interception risks compound the impact if the transport is not protected by TLS. MiddleBrick scans detect unauthenticated endpoints and can identify whether endpoints that accept Basic Auth are vulnerable to injection-like patterns by correlating spec definitions with runtime behavior. The scanner checks whether authentication handlers properly validate and parameterize inputs before constructing queries, flagging cases where user-controlled data reaches database-building code without sanitization.
In practice, this vulnerability maps to the OWASP API Top 10 category 'Broken Object Level Authorization' and can lead to unauthorized access or data leakage. Axum applications that parse headers manually and forward credentials to NoSQL backends must ensure strict type handling, avoid string-based query assembly, and use prepared patterns or typed structs that the database driver can safely encode.
Basic Auth-Specific Remediation in Axum — concrete code fixes
Remediation centers on never using raw header values directly in database queries. Instead, treat credentials as opaque identifiers and validate them against a parameterized data store. Use strong types and avoid string concatenation when building NoSQL filters.
Example of vulnerable code in Axum that combines Basic Auth with a MongoDB-like filter construction:
// Vulnerable: direct string usage in a filter
async fn login_handler(
Extension(pool): Extension>,
headers: HeaderMap,
) -> Result {
let auth = headers.get("authorization")
.ok_or((StatusCode::UNAUTHORIZED, "Missing auth".to_string()))?
.to_str().map_err(|_| (StatusCode::BAD_REQUEST, "Bad encoding".to_string()))?;
let creds = decode_basic(auth); // returns (username, password)
let username = creds.0;
let password = creds.1;
// Unsafe: building a filter by interpolating strings
let filter = doc! { "username": username, "password": password };
let user = collection.find_one(filter, None).await.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// ...
}
Correct approach using typed structures and parameterized checks:
// Safe: use typed structs and let the driver handle values
#[derive(Deserialize)]
struct Credentials {
username: String,
password: String,
}
async fn safe_login_handler(
Extension(pool): Extension>,
headers: HeaderMap,
) -> Result {
let auth = headers.get("authorization")
.ok_or((StatusCode::UNAUTHORIZED, "Missing authorization".into()))?
.to_str().map_err(|_| (StatusCode::BAD_REQUEST, "Invalid header".into()))?;
let creds = decode_basic(auth).map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid credentials".into()))?;
// Use a parameterized query via a typed struct or prepared statement
let user: Option = collection
.find_one(doc! { "username": &creds.username, "password_hash": hash_password(&creds.password) }, None)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
match user {
Some(u) => Ok(Json(u)),
None => Err((StatusCode::UNAUTHORIZED, "Invalid credentials".into())),
}
}
Additional measures include enforcing TLS, using hashed passwords, and validating input length and characters before using them in any backend call. MiddleBrick can verify that such patterns are in place by scanning the unauthenticated surface and noting whether endpoints leak credentials in error messages or accept injection-prone payloads.