GuardAPI Logo
GuardAPI
GuardAPI Logo GuardAPI

Fix NoSQL Injection in Actix Web

NoSQL injection in Actix Web occurs when raw BSON documents are constructed using untrusted input from `serde_json::Value` or loosely typed maps. Attackers leverage operators like `$gt`, `$ne`, or `$regex` to bypass logic. If you're piping raw JSON bodies directly into the `doc!` macro, you're handing over the keys to your database.

The Vulnerable Pattern

use actix_web::{post, web, HttpResponse, Responder};
use mongodb::bson::doc;

#[post(“/login”)] async fn login(db: web::Datamongodb::Database, body: web::Json<serde_json::Value>) -> impl Responder { // VULNERABLE: body[“username”] could be {“$ne”: null} // This allows an attacker to bypass authentication by providing a query operator instead of a string. let filter = doc! { “username”: body.get(“username”).unwrap_or(&serde_json::Value::Null), “password”: body.get(“password”).unwrap_or(&serde_json::Value::Null) };

match db.collection::<serde_json::Value>("users").find_one(filter, None).await {
    Ok(Some(_)) => HttpResponse::Ok().body("Logged in"),
    _ => HttpResponse::Unauthorized().finish(),
}

}

The Secure Implementation

The vulnerability exists because MongoDB's `doc!` macro accepts BSON types converted from `serde_json::Value`. When an attacker sends a JSON object where a string is expected, the driver interprets it as a query operator. For instance, sending `{"username": {"$ne": ""}}` makes the query match any user with a non-empty username. The fix is Type Safety: by defining a `struct` with `String` fields for your request body, `serde-json` will reject any input that isn't a literal string, effectively neutralizing operator injection at the deserialization layer.

use actix_web::{post, web, HttpResponse, Responder};
use mongodb::bson::doc;
use serde::Deserialize;

#[derive(Deserialize)] struct AuthRequest { username: String, password: String, }

#[post(“/login”)] async fn login(db: web::Datamongodb::Database, body: web::Json) -> impl Responder { // SECURE: Strict typing via AuthRequest struct ensures username and password are Strings. // Serde will fail to deserialize if the attacker sends an object like {“$ne”: ""}. let filter = doc! { “username”: &body.username, “password”: &body.password };

match db.collection::<serde_json::Value>("users").find_one(filter, None).await {
    Ok(Some(_)) => HttpResponse::Ok().body("Logged in"),
    _ => HttpResponse::Unauthorized().finish(),
}

}

System Alert • ID: 4413
Target: Actix Web API
Potential Vulnerability

Your Actix Web API might be exposed to NoSQL Injection

74% of Actix Web apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.

RUN FREE SECURITY DIAGNOSTIC
GuardLabs Engine: ONLINE

Free Tier • No Credit Card • Instant Report

Verified by Ghost Labs Security Team

This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.