HIGH axumsql injection blind

Sql Injection Blind in Axum

How Sql Injection Blind Manifests in Axum

Blind SQL injection in Axum applications occurs when user input is improperly concatenated into SQL queries without validation or parameterization, but the application does not return direct query results or error messages. Attackers infer information through boolean-based or time-based techniques, observing changes in response behavior such as HTTP status codes, response times, or content length. In Axum, this commonly appears in handler functions that extract query parameters, path variables, or JSON body fields and pass them directly to database query builders.

For example, consider an Axum endpoint that retrieves a user profile by ID from a PostgreSQL database using the tokio-postgres crate. If the ID is taken from a path parameter and inserted into a query string without sanitization, it creates a blind SQL injection vector:

use axum::{extract::Path, Json};
use tokio_postgres::{Client, NoTls};

async fn get_user(Path(id): Path) -> Json> {
    let (client, connection) = tokio_postgres::connect("host=localhost user=postgres", NoTls).await.unwrap();
    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("connection error: {}", e);
        }
    });

    // VULNERABLE: Direct string concatenation of user input
    let query = format!("SELECT id, username, email FROM users WHERE id = '{}'", id);
    let rows = client.query(&query, &[]).await.unwrap();
    
    // ... process rows into User struct
    Json(Some(User { /* ... */ }))
}

An attacker can exploit this by sending crafted IDs like ' OR 1=1-- or time-based payloads such as ' OR (SELECT pg_sleep(5))--. Since Axum returns the same success response regardless of data presence (or a generic 404), the application leaks information through response timing or subtle behavioral differences—classic blind SQL injection characteristics.

This pattern is particularly dangerous in Axum because the framework’s emphasis on performance and type safety can lead developers to overlook input validation in handler extraction, assuming that Rust’s ownership model prevents all injection flaws. However, SQL injection is a semantic issue, not a memory safety one, and requires explicit safeguards at the query construction layer.

Axum-Specific Detection

Detecting blind SQL injection in Axum applications requires observing behavioral anomalies in response to crafted inputs, since traditional error-based detection fails. middleBrick identifies these vulnerabilities during its unauthenticated black-box scan by injecting sequential probes that test for boolean-based and time-based inference vectors across all discoverable endpoints.

When scanning an Axum API, middleBrick sends payloads designed to trigger conditional delays or response variations. For boolean-based detection, it might inject ' AND 1=1-- versus ' AND 1=2-- and compare response bodies, status codes, or headers for statistically significant differences. For time-based detection, it uses payloads like ' OR (SELECT pg_sleep(3))-- and measures whether response times increase predictably.

These tests are performed without credentials or configuration—only the public URL is required. middleBrick’s parallel execution of 12 security checks ensures that SQL injection probes run concurrently with other tests (e.g., authentication bypass, IDOR), completing within 5–15 seconds. The scanner does not need to see source code or internal logs; it infers vulnerability from external behavior alone.

For instance, if an Axum endpoint at /api/products/{id} shows a 200ms baseline response but consistently returns in ~3200ms when the ID parameter contains a sleep payload, middleBrick flags this as a high-severity blind SQL injection finding. The resulting report includes the exact payload used, the observed delay, and the affected endpoint, enabling developers to reproduce and verify the issue.

This approach is especially effective for Axum applications where error messages are deliberately suppressed in production (a good security practice), rendering traditional scanners ineffective. middleBrick’s focus on the unauthenticated attack surface means it finds issues that internal testing might miss if auth gates are assumed to block exploitation—when in fact, many blind SQLi vectors exist on public endpoints.

Axum-Specific Remediation

Fixing blind SQL injection in Axum applications requires eliminating dynamic SQL construction by using parameterized queries or type-safe query builders. The solution lies not in input validation alone (though it helps), but in ensuring user data is never interpreted as SQL syntax. Axum handlers should delegate data access to repositories that use safe database interfaces.

The vulnerable code from the earlier example can be fixed by replacing string concatenation with a parameterized query using tokio-postgres's built-in support for typed parameters:

use axum::{extract::Path, Json};
use tokio_postgres::{Client, NoTls};

async fn get_user(Path(id): Path) -> Json> {
    let (client, connection) = tokio_postgres::connect("host=localhost user=postgres", NoTls).await.unwrap();
    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("connection error: {}", e);
        }
    });

    // FIXED: Parameterized query prevents SQL injection
    let query = "SELECT id, username, email FROM users WHERE id = $1";
    let rows = client.query(query, &[&id]).await.unwrap();
    
    // ... process rows into User struct
    Json(rows.first().map(|row| User {
        id: row.get(0),
        username: row.get(1),
        email: row.get(2),
    }))
}

Here, the $1 placeholder ensures the id value is treated strictly as data, not executable SQL. Even if id contains ' OR 1=1--, it is passed as a literal string value, neutralizing the attack.

For applications using an ORM like sqlx, which integrates naturally with Axum’s async ecosystem, the fix is even more robust due to compile-time checking:

use axum::{extract::Path, Json};
use sqlx::PgPool;

async fn get_user(Path(id): Path, pool: Extension) -> Json> {
    let user = sqlx::query_as!
        (

Note that changing the id extractor to i32 adds a layer of defense-in-depth: if the path cannot be parsed as an integer, Axum returns a 400 Bad Request before the handler runs. However, the core protection comes from sqlx's parameterized query, which would remain safe even if the type were String.

After applying these fixes, rescan the endpoint with middleBrick to confirm the blind SQL injection finding is resolved. The scanner will no longer detect anomalous behavior in response to injection payloads, confirming that user input is properly isolated from SQL command execution.

Frequently Asked Questions

Can Axum's built-in extraction or validation prevent blind SQL injection?
Axum's extraction (e.g., Path<T>, Query<T>) and validation (via Validator derive) help ensure input conforms to expected types or formats, but they do not prevent SQL injection on their own. If a validated string is later concatenated into a SQL query, injection remains possible. The core defense must be parameterized queries or type-safe query builders like sqlx or tokio-postgres with proper parameter binding—Axum extraction is a complementary layer, not a replacement for safe data access.
Does middleBrick need to see my Axum source code or database schema to detect blind SQL injection?
No. middleBrick performs unauthenticated black-box testing, meaning it only requires the public URL of your API. It detects blind SQL injection by analyzing external behavior—such as response times, status codes, or body differences—in response to crafted payloads. No source code, schema, credentials, or internal access is needed. This makes it suitable for scanning staging or production APIs without instrumentation.