Sql Injection in Actix with Api Keys
Sql Injection in Actix with Api Keys — how this specific combination creates or exposes the vulnerability
SQL Injection remains a critical risk in Actix-based Rust APIs when parameterized queries are not used, and exposing an API key–based authentication path can amplify both the likelihood and impact. In Actix web applications, route handlers often extract an API key from headers (e.g., X-API-Key) to identify the caller before processing a request. If the API key is used to select user data or tenant context from a database using string concatenation or interpolation, an attacker who can influence the API key (for example, via a compromised or leaked key) can leverage SQL Injection to manipulate the query logic.
Consider a scenario where the API key is directly interpolated into a SQL string to fetch a tenant ID. An attacker with a valid but low-privilege API key could modify the key to include SQL fragments, altering the query behavior. For example, using a key like abc' OR '1'='1 could transform the intended lookup into a condition that always matches, potentially bypassing intended access boundaries. More severely, an attacker could chain crafted input to perform UNION-based data exfiltration or execute administrative actions, especially if the application uses a highly privileged database user for connectivity.
The combination is particularly dangerous because API keys are often treated as secrets and logged or transmitted widely, increasing exposure. If an API key is leaked or brute-forced, an attacker gains a direct vector to probe the backend SQL layer. Actix handlers that construct SQL dynamically—such as using sqlx::query with string formatting—fail to separate code from data, enabling injection. Even when using query parameters in some parts of the code, inconsistent usage across dynamic table names or column identifiers may leave injection surfaces. This highlights the need to treat API key–driven data flows with the same rigor as any user-supplied input.
Api Keys-Specific Remediation in Actix — concrete code fixes
To mitigate SQL Injection in Actix when using API keys, enforce strict separation between SQL code and data by using parameterized queries for all values derived from the key or its associated metadata. Avoid using the API key directly in SQL string construction, even for selecting tenants or roles. Below are concrete, safe examples.
Insecure pattern (vulnerable)
use actix_web::{web, HttpResponse};
use sqlx::PgPool;
async fn get_tenant(
pool: web::Data,
headers: web::Header<String>,
) -> HttpResponse {
let api_key = headers.into_inner();
// UNSAFE: direct interpolation enables SQL Injection
let row = sqlx::query(&format!("SELECT tenant_id, role FROM tenants WHERE api_key = '{}'", api_key))
.fetch_one(&*pool)
.await;
match row {
Ok(r) => HttpResponse::Ok().json(r),
Err(_) => HttpResponse::Unauthorized().finish(),
}
}
Secure pattern (parameterized query)
use actix_web::{web, HttpResponse};
use sqlx::PgPool;
async fn get_tenant(
pool: web::Data<PgPool>,
headers: web::Header<String>,
) -> HttpResponse {
let api_key = headers.into_inner();
// SAFE: parameterized query prevents SQL Injection
let row = sqlx::query("SELECT tenant_id, role FROM tenants WHERE api_key = $1")
.bind(&api_key)
.fetch_optional(&*pool)
.await;
match row {
Ok(Some(r)) => HttpResponse::Ok().json(r),
Ok(None) => HttpResponse::Unauthorized().finish(),
Err(_) => HttpResponse::InternalServerError().finish(),
}
}
Advanced safe pattern (mapping key to tenant with strict validation)
use actix_web::{web, HttpResponse};
use serde::Deserialize;
use sqlx::PgPool;
#[derive(Deserialize)]
struct AuthHeader {
#[serde(rename = "X-API-Key")]
key: String,
}
async fn get_tenant_safe(
pool: web::Data<PgPool>,
hdr: web::Header<AuthHeader>,
) -> HttpResponse {
let api_key = &hdr.key;
// Enforce format constraints before using the key
if !api_key.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') {
return HttpResponse::BadRequest().body("Invalid API key format");
}
// Use parameterized query with a strict allowlist for returned columns
let row = sqlx::query(
"SELECT tenant_id, role FROM tenants WHERE api_key = $1 AND status = $2",
)
.bind(api_key)
.bind("active")
.fetch_optional(&*pool)
.await;
match row {
Ok(Some(r)) => HttpResponse::Ok().json(r),
Ok(None) => HttpResponse::Unauthorized().finish(),
Err(_) => HttpResponse::InternalServerError().finish(),
}
}
Additional guidance: use prepared statements or stored procedures where supported, and apply least-privilege database accounts so that even if an API key is compromised, the potential impact is limited. Regularly rotate keys and monitor usage for anomalies. These practices align with the checks in the middleBrick Security category, which can detect SQL Injection patterns and weak authentication handling.
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 |