Nosql Injection in Axum with Cockroachdb
Nosql Injection in Axum with Cockroachdb — how this specific combination creates or exposes the vulnerability
NoSQL Injection is a class of injection that targets databases with query languages that are not strictly typed or that accept structured input such as JSON/BSON. When an Axum service builds queries to CockroachDB using unsanitized user input, it can inadvertently change the structure or intent of the database operation. Although CockroachDB is a SQL-compatible database, it can be accessed through drivers and ORMs that allow document-style operations (e.g., via JSONB columns or through query builders that accept maps), which may expose NoSQL-like injection surfaces.
In Axum, handlers often receive JSON payloads that are deserialized into Rust structs before being used to construct queries. If developers pass user-controlled JSON fragments directly into database operations—such as embedding them into filter maps, dynamic SQL fragments, or JSONB path expressions—they may unintentionally allow attackers to inject operators or subdocuments that alter query logic. For example, a login handler that accepts JSON { "email": "...", "password": { "$ne": "" } } could bypass authentication if the application merges the incoming JSON into a query map without validation.
Another common scenario involves dynamic construction of SQL text in Axum using string concatenation or formatting with user input that targets CockroachDB. An attacker may supply values such as ' OR 1=1 -- inside a JSON field or URL parameter that becomes part of a query, leveraging type confusion between JSON values and SQL identifiers. Because CockroachDB supports JSONB and full-text search extensions, an application that interprets user input as JSON paths can be tricked into traversing unintended document structures, leading to data leakage or privilege escalation.
Middleware in Axum can inadvertently propagate these risks when request extraction is loosely typed. If route extractors or guards do not enforce strict schemas, untrusted JSON can flow into service layers where it influences how SQL is built for CockroachDB. Attack patterns such as authentication bypass, data exfiltration, and privilege escalation emerge when NoSQL-like injection is possible through JSONB columns, dynamic WHERE clauses, or improperly sanitized identifiers. Tools like middleBrick help detect these issues by scanning Axum endpoints that interact with CockroachDB, highlighting insecure deserialization and query construction practices that may enable injection.
Cockroachdb-Specific Remediation in Axum — concrete code fixes
Mitigating NoSQL Injection in Axum with CockroachDB centers on strict input validation, typed query interfaces, and avoiding dynamic query assembly with user-controlled data. Use strongly typed query builders or an ORM that supports parameterized statements, and never directly interpolate user input into SQL or JSONB expressions. Below are concrete, safe patterns for Rust Axum services that interact with CockroachDB.
1. Use typed SQLx queries with positional parameters
SQLx with CockroachDB ensures that values are sent separately from the query structure, preventing injection regardless of JSON content. Define a route that expects a strict DTO and uses parameterized SQL.
use axum::{routing::post, Router};
use serde::{Deserialize, Serialize};
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
#[derive(Deserialize)]
struct LoginBody {
email: String,
password: String,
}
#[derive(Serialize)]
struct LoginResponse {
user_id: i64,
}
async fn login_handler(
pool: PgPool,
body: axum::Json,
) -> Result<axum::Json<LoginResponse>, (axum::http::StatusCode, String)> {
let user = sqlx::query!(
"SELECT id, email FROM users WHERE email = $1 AND password_hash = crypt($2, password_hash)",
body.email,
body.password
)
.fetch_optional(&pool)
.await
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
match user {
Some(u) => Ok(axum::Json(LoginResponse { user_id: u.id })),
None => Err((axum::http::StatusCode::UNAUTHORIZED, "Invalid credentials".into())),
}
}
#[tokio::main]
async fn main() {
let pool: PgPool = PgPoolOptions::new()
.connect(&std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"))
.await
.expect("Failed to create pool");
let app = Router::new().route("/login", post(login_handler));
axum::Server::bind(&("0.0.0.0:3000"[..]).parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
2. Validate and sanitize JSONB inputs before use
If your application must accept JSON and store it in JSONB columns, validate the structure and avoid using raw user input as JSON path expressions. Use serde_json to enforce a schema and extract only safe values.
use axum::{routing::post, Json};
use serde_json::{json, Map, Value};
use sqlx::postgres::types::Json as SqlJson;
use sqlx::PgPool;
async fn update_profile_handler(
pool: PgPool,
Json(payload): Json<Value>,
) -> Result<Json<Value>, (axum::http::StatusCode, String)> {
// Strict schema validation
let email = payload.get("email")
.and_then(Value::as_str)
.ok_or_else(|| (axum::http::StatusCode::BAD_REQUEST, "email required".into()))?;
// Build JSONB update using sqlx::types::Json to safely serialize
let update_json = json!({
"email": email,
"preferences": payload.get("preferences").cloned().unwrap_or(json!({}))
});
sqlx::query(
"UPDATE profiles SET details = details || $1 WHERE email = $2"
)
.bind(SqlJson(update_json))
.bind(email)
.execute(&pool)
.await
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(json!({ "status": "ok" })))
}
3. Avoid dynamic SQL with user-controlled identifiers
Never interpolate table or column names from user input. If dynamic schema changes are required, use a strict allowlist and parameterized queries for values only.
let allowed_column = match user_supplied_column.as_str() {
"username" | "email" | "created_at" => user_supplied_column,
_ => return Err((axum::http::StatusCode::BAD_REQUEST, "invalid column".into())),
};
// Still parameterize the value, not the identifier
sqlx::query(&format!("SELECT * FROM users ORDER BY {} LIMIT 1", allowed_column))
.bind(value_param)
.fetch_one(&pool).await;
By combining strict deserialization, parameterized SQL via SQLx, and schema validation, Axum services can safely interact with CockroachDB without introducing NoSQL Injection vectors. middleBrick scans can verify these protections by checking for dynamic query construction and unsafe deserialization patterns.