Sql Injection in Actix with Hmac Signatures
Sql Injection in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
In Actix web applications, Sql Injection can occur when user-controlled input is concatenated into SQL queries. Using Hmac Signatures to authenticate requests does not inherently protect against Sql Injection; it only verifies that the request originates from a party that knows the shared secret. If the application uses the Hmac signature to select or build dynamic queries without sanitizing inputs, the signature validates a tamper-proof payload that may still carry malicious SQL fragments.
Consider an endpoint that accepts an item_id query parameter and an X-API-Signature header. The server verifies the Hmac over the canonical string composed of selected headers and the query string, then builds a SQL query like format('SELECT * FROM items WHERE id = %L', item_id) using string interpolation or concatenation. Even with a valid Hmac, if item_id is not strictly validated as an integer, an attacker can supply 1 OR 1=1. The Hmac will verify successfully because the attacker can compute the signature if the secret is leaked or if the signing scope is too broad (e.g., including mutable query parameters that should not be signed). This results in unauthorized data access or data manipulation via Sql Injection.
Another scenario involves dynamic table or column names derived from signed parameters. For example, an API might accept a sort parameter included in the Hmac payload to determine ordering. If the application directly interpolates this value into SQL as ORDER BY {sort}, an attacker can inject SQL fragments even when the Hmac is valid. The root cause is trusting the Hmac for authentication while failing to enforce strict allowlists, type checks, or parameterized queries for all data used in SQL construction.
In the context of middleBrick’s checks, this combination is flagged under Input Validation and BOLA/IDOR. The scanner tests whether tampering with signed parameters leads to altered SQL execution paths, and whether the application conflates authentication of the request with safety of the data it references. Proper separation of concerns—using Hmac for request integrity and placeholders or ORM parameterization for SQL safety—is essential to mitigate Sql Injection in Actix APIs that employ Hmac Signatures.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
Remediation focuses on strict input validation, canonicalization of signed data, and using parameterized SQL queries. Do not rely on Hmac to sanitize or type-check data; treat it solely as a mechanism to prove request integrity. Below are concrete Actix examples that demonstrate secure patterns.
First, validate and parse inputs before signing or verification. For numeric IDs, use strict parsing and reject non-integer values:
use actix_web::{web, HttpResponse, Result};
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
fn verify_hmac(body: &str, received_sig: &str, secret: &[u8]) -> bool {
let mut mac = HmacSha256::new_from_slice(secret).expect("Hmac can take key of any size");
mac.update(body.as_bytes());
match hex::decode(received_sig) {
Ok(sig_bytes) => mac.verify_slice(&sig_bytes).is_ok(),
Err(_) => false,
}
}
async fn get_item(
web::Query(params): web::Query<std::collections::HashMap<String, String>>,
web::Header("x-api-signature") signature: String,
) -> Result<HttpResponse> {
let item_id_str = params.get("item_id").ok_or_else(|| HttpResponse::BadRequest().body("missing item_id"))?;
let item_id: i64 = item_id_str.parse().map_err(|_| HttpResponse::BadRequest().body("invalid item_id"))?;
// Canonical string for signing should exclude the signature itself and include only safe components
let canonical = format!("/items?item_id={}", item_id);
// verification omitted for brevity; assume verify_hmac returns true
let sql = format!(r#"SELECT * FROM items WHERE id = {}"#, item_id); // UNSAFE: still using format
// Use a proper query builder or parameterized query instead:
// Recommended: use sqlx or diesel with bound parameters
Ok(HttpResponse::Ok().body("ok"))
}
Second, use parameterized queries with a library such as sqlx to ensure inputs are never directly interpolated:
use actix_web::web;
use sqlx::postgres::PgPool;
async fn get_item_safe(
pool: web::Data<PgPool>,
web::Query(params): web::Query<std::collections::HashMap<String, String>>,
web::Header("x-api-signature") signature: String,
) -> actix_web::Result<impl actix_web::Responder> {
let item_id_str = params.get("item_id").ok_or_else(|| actix_web::error::ErrorBadRequest("missing item_id"))?;
let item_id: i64 = item_id_str.parse().map_err(|_| actix_web::error::ErrorBadRequest("invalid item_id"))?;
// Verify Hmac using canonical representation that includes only safe components
// ... verify_hmac(...)
// Safe query with bound parameter
let row: (i64, String) = sqlx::query_as("SELECT id, name FROM items WHERE id = $1")
.bind(item_id)
.fetch_one(pool.as_ref())
.await
.map_err(|_| actix_web::error::ErrorInternalServerError("db error"))?;
Ok(actix_web::HttpResponse::Ok().json(row))
}
Third, if you must sign dynamic values (e.g., sorting fields), enforce an allowlist:
const ALLOWED_SORT_FIELDS: &[&str] = &["name", "created_at", "updated_at"];
fn validate_sort_field(value: &str) -> Option&str {
if ALLOWED_SORT_FIELDS.contains(&value) {
Some(value)
} else {
None
}
}
async fn list_items(
web::Query(params): web::Query<std::collections::HashMap<String, String>>,
web::Header("x-api-signature") signature: String,
pool: web::Data<PgPool>,
) -> actix_web::Result<impl actix_web::Responder> {
let sort_field = params.get("sort").map(|s| s.as_str()).unwrap_or("created_at");
let sort_field = validate_sort_field(sort_field).ok_or_else(|| actix_web::error::ErrorBadRequest("invalid sort field"))?;
// Build query safely: use parameterized values for data, and a strict allowlist for identifiers
let query = format!(r#"SELECT id, name FROM items ORDER BY "{}" LIMIT $1", sort_field); // sort_field is validated
// Execute with a bound limit parameter
let rows = sqlx::query(&query)
.bind(10_i64)
.fetch_all(pool.as_ref())
.await
.map_err(|_| actix_web::error::ErrorInternalServerError("db error"))?;
Ok(actix_web::HttpResponse::Ok().json(rows))
}
Key takeaways: Hmac Signatures ensure request integrity but do not replace input validation, type checks, or parameterized SQL. Use strict allowlists for dynamic identifiers, parse and validate all inputs before signing, and rely on placeholders or ORM mechanisms for SQL construction. middleBrick scans can detect whether signed parameters influence unsafe SQL construction and will highlight these findings under Input Validation and BOLA/IDOR categories.
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 |