Sql Injection in Axum
How Sql Injection Manifests in Axum
SQL Injection vulnerabilities in Axum applications typically occur when user input is incorporated into SQL queries without proper sanitization. In Axum, this often manifests through dynamic query construction using query parameters, path parameters, or JSON request bodies.
Consider this vulnerable Axum handler:
async fn get_user_by_id(
Extension(pool): Extension,
id: String,
) -> Result<Json<User>> {
let query = format!("SELECT * FROM users WHERE id = '{}'", id);
let row = sqlx::query_as(&query)
.fetch_one(pool)
.await?;
Ok(Json(row))
}
An attacker could exploit this by sending id values like 1' OR '1'='1, causing the query to return all users. The vulnerability stems from using string interpolation (format!) to construct SQL queries.
Another common pattern in Axum involves building queries with conditional logic:
async fn search_users(
Extension(pool): Extension<PgPool>,
search: String,
category: String,
) -> Result<Json<Vec<User>>> {
let mut query = String::from("SELECT * FROM users WHERE 1=1");
if !search.is_empty() {
query.push_str(&format!(" AND name LIKE '%{}%'", search));
}
if !category.is_empty() {
query.push_str(&format!(" AND category = '{}'", category));
}
let rows = sqlx::query_as(&query)
.fetch_all(pool)
.await?;
Ok(Json(rows))
}
This approach is fundamentally unsafe. An attacker could manipulate the search or category parameters to inject arbitrary SQL, potentially dropping tables, modifying data, or extracting sensitive information.
Even with parameterized queries, developers sometimes make mistakes. For example:
async fn get_user_by_id(
Extension(pool): Extension<PgPool>,
id: String,
) -> Result<Json<User>> {
let query = format!("SELECT * FROM users WHERE id = $1", id);
let row = sqlx::query_as("SELECT * FROM users WHERE id = $1")
.bind(id)
.fetch_one(pool)
.await?;
Ok(Json(row))
}
This code appears safe but is vulnerable to SQL injection because the format! macro is used unnecessarily, creating confusion about whether parameterization is actually being used.
Axum-Specific Detection
Detecting SQL Injection in Axum applications requires examining both the source code and runtime behavior. For source code analysis, look for these patterns:
grep -r "format!" src/ | grep -E "(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP)"
This command finds dynamic SQL construction patterns. More specifically, search for:
grep -r "query_as(" src/ | grep -E "(format|push_str|to_string)"
middleBrick's scanner can automatically detect these patterns by analyzing your API endpoints. When you scan an Axum application, middleBrick examines the request handling code and identifies unsafe query construction patterns.
For runtime detection, middleBrick performs active probing by sending specially crafted payloads to test for SQL injection vulnerabilities. For example, it might send:
POST /api/users
Content-Type: application/json
{"search": "' OR '1'='1", "category": "' OR '1'='1"}
If the API returns unexpected results or error messages containing SQL syntax, this indicates a potential vulnerability. middleBrick's LLM/AI security module also checks for SQL injection in AI-powered endpoints, testing whether malicious SQL can be injected through natural language queries.
Another detection technique involves examining error responses. If your Axum application returns detailed database error messages to clients, this can leak information about your database structure and help attackers craft more effective SQL injection attacks.
Axum-Specific Remediation
The proper way to prevent SQL Injection in Axum is to use parameterized queries exclusively. Here's the secure version of the earlier examples:
async fn get_user_by_id(
Extension(pool): Extension<PgPool>,
id: String,
) -> Result<Json<User>> {
let row = sqlx::query_as("SELECT * FROM users WHERE id = $1")
.bind(id)
.fetch_one(pool)
.await?;
Ok(Json(row))
}
For conditional queries, use parameterized conditions:
async fn search_users(
Extension(pool): Extension<PgPool>,
search: Option<String>,
category: Option<String>,
) -> Result<Json<Vec<User>>> {
let mut query = String::from("SELECT * FROM users WHERE 1=1");
let mut params = Vec::new();
if let Some(search) = search {
query.push_str(" AND name ILIKE $");
params.push({"%" .to_string() + &search + "%"});
}
if let Some(category) = category {
query.push_str(" AND category = $");
params.push(category);
}
let rows = sqlx::query_as(&query)
.bind_all(params)
.fetch_all(pool)
.await?;
Ok(Json(rows))
}
middleBrick's CLI tool can help verify your remediation:
middlebrick scan https://your-axum-api.com
This scans your running API and provides a security score with specific findings about SQL injection vulnerabilities. The tool checks whether your queries are properly parameterized and whether error messages reveal sensitive information.
For additional protection, implement input validation using Axum's extractors:
use axum::extract::Path;
use serde::Deserialize;
#[derive(Deserialize)]
struct UserIdPath {
id: i32, // Only accept numeric IDs
}
async fn get_user_by_id(
Extension(pool): Extension<PgPool>,
Path(UserIdPath { id }): Path<UserIdPath>,
) -> Result<Json<User>> {
let row = sqlx::query_as("SELECT * FROM users WHERE id = $1")
.bind(id)
.fetch_one(pool)
.await?;
Ok(Json(row))
}
This approach combines type safety with parameterized queries, providing defense in depth against SQL injection attacks.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |