Sql Injection Union in Axum (Rust)
Sql Injection Union in Axum with Rust — how this specific combination creates or exposes the vulnerability
SQL injection via UNION-based techniques exploits dynamic query construction in Axum handlers written in Rust. When user-controlled input is concatenated into SQL strings without parameterization, an attacker can append a UNION SELECT payload to read from unrelated tables, bypass authentication, or extract schema details. This risk is present whether you use raw sqlx or an ORM that ultimately builds string-based queries.
Consider an endpoint that retrieves a user by username and appends sorting options directly into the SQL string:
// Unsafe: string concatenation enables UNION-based injection
async fn get_user(conn: &PgPool, username: &str) -> Result<User, Error> {
let query = format!("SELECT id, name, email FROM users WHERE name = '{}' ORDER BY {}", username, "created_at");
sqlx::query_as(&query).fetch_one(conn).await
}
An attacker can supply admin' UNION SELECT id, password, email FROM users-- as the username, turning the query into:
SELECT id, name, email FROM users WHERE name = 'admin' UNION SELECT id, password, email FROM users-- ORDER BY created_at
This exposes data from other users. In Rust, using sqlx::query with format strings does not automatically separate SQL structure from data, making UNION-based injection possible. The scanner’s input validation checks flag such patterns by correlating untrusted input with SQL syntax regions and detecting suspicious UNION keywords in runtime probes.
Axum’s routing and extractor model does not inherently protect against this; developers must ensure all SQL is parameterized. Even seemingly benign dynamic ORDER BY or SELECT clauses should be handled via allowlists, not string interpolation. The scanner’s property authorization and input validation checks highlight these weaknesses by mapping data flow from HTTP extractors into SQL execution paths.
Additionally, if the API exposes stack traces or verbose errors, an attacker can refine UNION payloads to infer column counts and types, leading to more precise data exfiltration. This is especially relevant when combined with other checks such as data exposure and encryption, where improperly handled responses may leak sensitive information. The LLM/AI security checks further verify whether models interacting with your API could be tricked into aiding injection via crafted prompts that manipulate error messages or schema introspection.
Rust-Specific Remediation in Axum — concrete code fixes
Remediation centers on strict separation of SQL code and data using parameterized queries and allowlists for non-value components. In Rust with sqlx, always use query parameters for user input and avoid format strings for SQL structure.
Safe Axum handler using parameterized queries:
use axum::extract::Query;
use serde::Deserialize;
use sqlx::PgPool;
#[derive(Deserialize)]
struct UserParams {
name: String,
sort_field: String, // validate against an allowlist
}
async fn get_user(
Query(params): Query<UserParams>,
pool: &State<PgPool>
) -> Result<impl IntoResponse, (StatusCode, String)> {
// Allowlist for dynamic SQL parts
let order_field = match params.sort_field.as_str() {
"created_at" | "name" | "email" => params.sort_field,
_ => return Err((StatusCode::BAD_REQUEST, "Invalid sort field".into())),
};
// Parameterized query for values; string interpolation only for trusted allowlist values
let query = format!(
"SELECT id, name, email FROM users WHERE name = $1 ORDER BY {}",
order_field
);
let user = sqlx::query_as(&query)
.bind(¶ms.name)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(user))
}
Key points:
- Use
$1,$2style placeholders for all user-supplied values; never embed them viaformat!or string concatenation. - For dynamic identifiers (e.g., column or table names), use an allowlist pattern as shown; do not pass raw user input into SQL structure.
- Leverage Rust’s type system:
sqlx::query_asdeserializes rows into structs, reducing manual parsing errors. - Combine with the CLI (
middlebrick scan <url>) to validate that your handlers no longer expose concatenated SQL paths. The dashboard can track risk scores over time to confirm remediation.
For broader protection, integrate the GitHub Action to fail CI/CD pipelines when risk scores drop below your chosen threshold, ensuring new endpoints follow the same safe patterns. The MCP Server allows you to scan APIs directly from your Rust development environment, surfacing issues before code is committed.