Prototype Pollution in Axum with Cockroachdb
Prototype Pollution in Axum with Cockroachdb — how this specific combination creates or exposes the vulnerability
Prototype pollution in an Axum application using CockroachDB typically arises when user-controlled input is merged into application state or query-building logic before being used in database operations. Axum, being a Rust web framework, does not inherently mutate JavaScript prototypes, but the term here refers to unintended modification of application-level data structures that later influence how queries are constructed or how parameters are validated. If request data is deserialized into a generic map (e.g., serde_json::Map) and then shallowly merged with default configuration objects, an attacker can inject keys such as __proto__, constructor, or other properties that affect equality checks or serialization behavior in downstream logic.
When combined with CockroachDB, the risk shifts to how these polluted structures influence SQL generation or parameter handling. For example, if Axum builds dynamic filters for CockroachDB queries by iterating over a polluted JSON object, an attacker-controlled key could change the semantics of a WHERE clause, potentially bypassing intended filters or causing unexpected row exposure. Although CockroachDB itself does not execute JavaScript or use prototypes, the application-layer misuse of merged objects can lead to insecure query patterns, such as concatenating user input into SQL strings or misapplying role-based access rules derived from the polluted state.
Consider an endpoint that merges incoming JSON with a base filter map before passing it to CockroachDB:
let base_filter = json!({ "tenant_id": "acme" });
let user_filter: serde_json::Value = extract_user_input();
let merged = merge_json(&base_filter, &user_filter); // unsafe merge
let query = format!("SELECT * FROM accounts WHERE {}", build_conditions(&merged));
If user_filter contains { "id": "1 OR 1=1" }, and build_conditions does not enforce strict key validation, the resulting SQL may bypass tenant isolation. This is not a database vulnerability but an application logic flaw that becomes more impactful because CockroachDB executes the constructed query as written. The unique aspect of using middleBrick with this stack is its ability to detect insecure dynamic query construction and input validation gaps during unauthenticated scans, even when OpenAPI specs describe the endpoint as parameterized.
Additionally, if the Axum service exposes an unauthenticated endpoint that echoes back query-building logic or schema details, an attacker could use prototype-polluted inputs to probe for conditional behaviors or error messages that reveal CockroachDB schema structure. This aligns with middleBrick’s checks for Input Validation and Data Exposure, which run in parallel and can highlight missing schema constraints or overly permissive deserialization in the API surface.
Cockroachdb-Specific Remediation in Axum — concrete code fixes
Secure remediation focuses on strict input validation, avoiding dynamic query assembly, and isolating user data from structural logic. In Axum, prefer strongly typed extractors over generic JSON merging, and use parameterized queries with the postgres or sqlx crate targeting CockroachDB.
1. Use typed structures and reject unknown fields:
#[derive(serde::Deserialize)]
struct AccountFilter {
tenant_id: String,
#[serde(flatten)]
extra: HashMap<String, String>, // explicitly allow extra if needed
}
async fn list_accounts(
Extension(pool): Extension<Arc<Pool>>,
Json(payload): Json<AccountFilter>,
) -> Result<Json<Vec<Account>> > {
let rows = pool
.fetch("SELECT id, name FROM accounts WHERE tenant_id = $1", &[&payload.tenant_id])
.await?;
Ok(Json(rows))
}
This ensures that only known fields are processed and prevents prototype-style keys from altering behavior.
2. Build queries with a builder pattern that validates keys and uses placeholders:
fn build_conditions(filters: &serde_json::Map<String, serde_json::Value>) -> (String, Vec<Box<dyn postgres_types::ToSql + Send + Sync>>) {
let mut conditions = Vec::new();
let mut params: Vec<Box<dyn postgres_types::ToSql + Send + Sync>> = Vec::new();
for (key, value) in filters {
if ["status", "created_at"].contains(&key.as_str()) {
conditions.push(format!("{} = ${}", key, params.len() + 1));
params.push(Box::new(value.as_str().unwrap_or("")));
}
}
let clause = if conditions.is_empty() { "TRUE".to_string() } else { conditions.join(" AND ") };
(clause, params)
}
3. Use CockroachDB-compatible parameterized queries via sqlx:
let pool = &app_state.cockroach_pool;
let records = sqlx::query_as!(Account,
"SELECT id, name, status FROM accounts WHERE tenant_id = $1 AND status = $2",
tenant_id,
status
)
.fetch_all(pool)
.await?;
4. Add middleware to reject inputs containing prototype-pollution patterns:
fn validate_no_pollution(value: &serde_json::Value) -> Result<(), &str> {
if let Some(obj) = value.as_object() {
for key in obj.keys() {
if key == "__proto__" || key == "constructor" || key == "prototype" {
return Err("Invalid property name");
}
}
}
Ok(())
}
By combining these practices, the Axum service ensures that CockroachDB receives only safe, parameterized inputs. MiddleBrick can then verify that the API correctly rejects malformed inputs and does not expose internal logic in error responses, aligning with its checks for Input Validation and Data Exposure.