Injection Flaws in Axum with Cockroachdb
Injection Flaws in Axum with Cockroachdb — how this specific combination creates or exposes the vulnerability
Injection flaws occur when untrusted data is sent to an interpreter as part of a command or query. In an Axum application using CockroachDB, the most common vector is constructing SQL strings by concatenating user input rather than using parameterized statements. CockroachDB, while PostgreSQL-wire compatible, enforces strict type handling and prepared statement semantics; building ad‑hoc SQL with string interpolation bypasses these safeguards and can lead to SQL injection, authentication bypass, or data exfiltration.
For example, concatenating a user-supplied identifier directly into a SQL string exposes the endpoint to classic injection. Axum extractors provide the raw values; if these are embedded into a query string before sending to CockroachDB, the database receives a single combined string, and its parser may interpret parts of that string as executable SQL. Attackers can supply crafted payloads that change query intent, such as appending OR '1'='1' to escalate privileges or extract rows they should not see.
In a layered stack, Axum routes deserialise JSON or form data into Rust structs; if validation is limited to basic presence or type checks, maliciously crafted strings can pass through. When such values are forwarded verbatim to CockroachDB, the lack of parameterization means the database cannot distinguish data from commands. This is especially risky when dynamic table or column names are interpolated, as CockroachDB does not support parameterizing identifiers, and naive string replacement will almost certainly introduce injection.
Consider an endpoint that builds a query like format!("SELECT * FROM accounts WHERE user_id = {}", user_id). If user_id comes from the request and contains SQL metacharacters or crafted syntax, the resulting statement can return or modify unintended data. An attacker might supply 1; DROP TABLE sessions; -- in a poorly validated field, and if the string is directly embedded, CockroachDB may execute multiple statements depending on driver configuration and parsing boundaries.
Additionally, logging or error messages that echo user input can aid reconnaissance. If Axum returns detailed database errors to clients, attackers can iteratively probe injection behavior. Combined with CockroachDB’s strict SQL grammar, this can reveal schema details or confirm successful manipulation. The key takeaway is that Axum provides the request pipeline; ensuring data is never interpreted as code before reaching CockroachDB is essential to prevent injection.
Cockroachdb-Specific Remediation in Axum — concrete code fixes
Use parameterized queries with the CockroachDB Rust driver to ensure user input is always treated as data. Avoid any string formatting that inserts values directly into SQL text. Below are concrete, working examples for Axum handlers.
1) Simple parameterized query
use axum::{routing::get, Router};
use cockroach_client::Client;
use serde::Deserialize;
#[derive(Deserialize)]
struct Lookup {
user_id: i64,
}
async fn get_account(
user_id: axum::extract::Query,
client: axum::extract::State<Client>,
) -> Result<String, (axum::http::StatusCode, String)> {
// Correct: parameter marker $1, value supplied separately
let row = client
.query_one(
"SELECT email FROM accounts WHERE user_id = $1",
&[&user_id.user_id],
)
.await
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let email: String = row.get(0);
Ok(email)
}
fn app() -> Router {
let client = Client::new("postgresql://...").await.unwrap();
Router::new()
.route("/account", get(get_account))
.with_state(client)
}
2) Dynamic table/column names (non-parameterizable)
Identifiers cannot be parameterized; you must validate them against a strict allowlist before interpolation.
async fn get_custom_table(
table_name: axum::extract::Query<TableQuery>,
client: axum::extract::State<Client>,
) -> Result<String, (axum::http::StatusCode, String)> {
const ALLOWED: &[&str] = "users,products,orders".split(',').collect::<Vec<&str>>();
if !ALLOWED.contains(&table_name.table.as_str()) {
return Err((axum::http::StatusCode::BAD_REQUEST, "invalid table".into()));
}
// Safe: table_name is vetted
let sql = format!("SELECT * FROM {} WHERE id = $1", table_name.table);
let row = client.query_one(&sql, &[&table_name.id]).await?;
// ...
Ok(row.get(0))
}
3) JSONB field updates with dynamic keys
When updating JSONB columns, use CockroachDB’s jsonb_set or build the update with placeholders for values only; never interpolate key names without validation.
async fn update_profile(
profile: axum::extract::Json<ProfileUpdate>,
client: axum::extract::State<Client>,
) -> Result<(), (axum::http::StatusCode, String)> {
// Example: set specific fields while ensuring keys are controlled
let sql = "UPDATE profiles SET data = jsonb_set(data, '{preferences}', $1, true) WHERE id = $2";
client
.execute(sql, &[&profile.preferences, &profile.id])
.await
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(())
}
Always prefer parameterized values for data, and rigorously validate any identifier against a fixed list. These practices align with OWASP API Top 10 A03:2023 Injection and help ensure that Axum+CockroachDB interactions remain resilient against injection attempts.