HIGH sql injectionaxummutual tls

Sql Injection in Axum with Mutual Tls

Sql Injection in Axum with Mutual Tls

SQL injection in Axum when mutual TLS is used is not a problem with TLS itself, but a result of how developers integrate authentication state into query construction. Mutual TLS provides strong client authentication, which often leads developers to trust the identity of the client and mistakenly treat headers, certificates, or claims as safe data for database queries. This trust boundary mismatch is where SQL injection can be introduced.

Consider an Axum handler that retrieves a user profile using a subject identifier obtained from the client certificate. If the subject (e.g., the certificate common name) is interpolated directly into a SQL string, an attacker who can present any valid client certificate can supply a malicious subject designed to alter query logic:

// Risky: using certificate subject directly in SQL without validation
async fn get_user_profile(
    Extension(pool): Extension>>
    client_cert: Option>,
) -> Result, (StatusCode, String)> {
    let subject = client_cert
        .ok_or((StatusCode::UNAUTHORIZED, "missing client cert"))?
        .subject
        .to_string();
    let sql = format!("SELECT id, name FROM users WHERE subject = '{subject}'");
    let row = pool.query_one(&sql, &[]).await.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    // ...
}

Although the client is authenticated via mutual TLS, the subject string is not validated or parameterized, enabling classic SQL injection patterns such as ' OR '1'='1 in the certificate subject. This maps directly to the OWASP API Top 10 A03:2021 – Injection and can lead to unauthorized data access or data loss.

Another scenario involves query parameters that developers assume are safe because the request arrived over a mutually authenticated channel. An endpoint that accepts a path or query parameter and concatenates it into SQL is vulnerable regardless of the TLS client identity:

// Risky: concatenating query parameter into SQL
async fn search_items(
    Extension(pool): Extension>>,
    Query(params): Query,
    client_cert: Option>,
) -> Result>, (StatusCode, String)> {
    // Mutual TLS does not sanitize 'params.query'
    let sql = format!("SELECT * FROM items WHERE name LIKE '%{}%' ORDER BY {}", params.query, params.sort_column);
    let rows = pool.query(&sql, &[]).await.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    // ...
}

Here, mutual TLS confirms who the client is, but it does not prevent malicious input in params.query or params.sort_column. The presence of mTLS can create a false sense of security, leading to neglected input validation and SQL injection via BFLA/Privilege Escalation if different permission levels are assumed based on cert identity.

SSRF and unsafe consumption concerns also intersect here: if the SQL layer returns sensitive metadata or schema details, an attacker can leverage crafted input to exfiltrate information even when mTLS is enforced. This aligns with Data Exposure and Unsafe Consumption checks in middleBrick’s 12 parallel security checks, which analyze how findings map to frameworks like PCI-DSS and GDPR.

Mutual Tls-Specific Remediation in Axum

Remediation centers on treating all data—whether from certificates, headers, or query parameters—as untrusted and enforcing strict parameterization and validation. Do not construct SQL by string interpolation, even when client identity has been verified.

Use prepared statements with typed parameters for all SQL operations. In Axum with sqlx, this means using placeholders and binding values explicitly:

// Safe: parameterized query with certificate subject validation
async fn get_user_profile_safe(
    Extension(pool): Extension>>,
    client_cert: Option>,
) -> Result, (StatusCode, String)> {
    let subject = client_cert
        .ok_or((StatusCode::UNAUTHORIZED, "missing client cert"))?
        .subject
        .to_string();
    // Validate subject format before using it
    if !subject.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-') {
        return Err((StatusCode::BAD_REQUEST, "invalid subject".to_string()));
    }
    let row = pool
        .query_one("SELECT id, name FROM users WHERE subject = $1", &[&subject])
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    Ok(Json(UserProfile { id: row.get(0), name: row.get(1) }))
}

For dynamic sorting and searching, validate against an allowlist and use parameterized queries with proper casting:

// Safe: validated sort column and parameterized LIKE pattern
async fn search_items_safe(
    Extension(pool): Extension>>,
    Query(params): Query,
    client_cert: Option>,
) -> Result>, (StatusCode, String)> {
    let allowed_columns = ["name", "created_at", "updated_at"];
    if !allowed_columns.contains(¶ms.sort_column) {
        return Err((StatusCode::BAD_REQUEST, "invalid sort column".to_string()));
    }
    let pattern = format!("%{}%", params.query.replace('%', "\x25"));
    let rows = pool
        .query(
            &format!("SELECT * FROM items WHERE name LIKE ${} ORDER BY {} ASC", 1, params.sort_column),
            &[&pattern],
        )
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    Ok(Json(rows))
}

These examples demonstrate concrete Axum patterns that align with middleBrick’s findings remediation guidance. Even with mutual TLS enforcing transport-layer identity, SQL safety requires disciplined use of parameterization, input validation, and allowlists. middleBrick’s scans can highlight such issues by correlating runtime behavior with OpenAPI specifications and detecting points where data flows into SQL execution paths without proper safeguards.

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Does mutual TLS prevent SQL injection in Axum?
No. Mutual TLS authenticates the client but does not sanitize inputs. SQL injection is prevented by using parameterized queries and strict input validation, not by transport-layer authentication.
How can I detect SQL injection risks in an Axum API scanned by middleBrick?
middleBrick scans unauthenticated attack surfaces and maps findings to frameworks like OWASP API Top 10. Review per-category breakdowns in the Web Dashboard or CLI JSON output to locate injection-prone endpoints and apply parameterized queries and input validation.