Time Of Check Time Of Use in Actix with Hmac Signatures
Time Of Check Time Of Use in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Time Of Check Time Of Use (TOCTOU) is a class of race condition where the state of a resource changes between a check and its subsequent use. In Actix web applications that use Hmac Signatures for request authentication, TOCTOU can occur when authorization logic first validates a signature or associated metadata (a check), and then later uses that authorization to process a sensitive operation (a use), with an attacker able to mutate the underlying resource between those steps.
Consider an endpoint that accepts an Hmac-signed request identifying a resource ID and an action (e.g., cancel an order). A naive implementation might verify the Hmac signature to ensure integrity and origin, record the resource ID, then perform a database read to check the current status (e.g., order.status == "pending") before proceeding to cancel. The check (status is pending) and the use (cancel the order) are separated by time and possibly other logic. If the order status can change via another request or side channel after the status check but before the cancellation logic executes, the authorization that was valid at check time may no longer be appropriate at use time. This is TOCTOU.
Hmac Signatures themselves do not prevent TOCTOU; they ensure the request payload and headers have not been altered in transit. They do not guarantee that the resource state referenced in the signed payload remains unchanged between verification and usage. An attacker who can influence or race the resource state (for example, by rapidly updating the order status via a separate, authorized session) can exploit the window to cause the server to use an outdated check and perform an unsafe operation. In Actix, this often manifests in handlers that read state, validate Hmac, and then write or mutate the same state without holding a consistent lock or revalidating immediately prior to the write.
An example scenario: a financial API endpoint signed with Hmac allows a transfer request specifying from, to, and amount. The handler verifies the Hmac, checks that from account has sufficient balance (check), and then performs the transfer (use). If the balance can be changed between the check and the use (e.g., another concurrent request or a queued event), the handler may proceed based on stale balance data, leading to over-transfer or race-induced inconsistency. This aligns with common web security risks around concurrency and integrity checks, even though the signature itself remains valid.
In Actix, because handlers are asynchronous and may be executed across multiple worker threads, the interval between signature verification and resource use can be magnified if shared state is accessed without proper synchronization. TOCTOU is not a flaw in Hmac algorithms, but a design and concurrency issue in how state checks and uses are composed. Mitigations must ensure that the check and use are performed as a single, atomic operation or that the state is locked or revalidated immediately before the use, regardless of the Hmac verification step.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
To mitigate TOCTOU when using Hmac Signatures in Actix, restructure handlers so that validation and state mutation are performed atomically or with state held across the check-use boundary. Avoid reading state to make authorization decisions and then later mutating that same state without revalidation. Instead, incorporate necessary state checks into the verification step or use short-lived locks that span both check and use.
Below are concrete Actix examples that demonstrate the problem and the secure pattern.
Vulnerable pattern (TOCTOU risk)
use actix_web::{web, HttpResponse, Result};
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
async fn cancel_order_vulnerable(
payload: web::Json<CancelRequest>,
headers: web::Header<actix_web::http::header::Authorization>,
) -> Result<HttpResponse> {
// 1) Extract and verify Hmac signature (check)
let signature = headers.to_string();
let mut mac = HmacSha256::new_from_slice(b"secret_key")?;
mac.update(payload.as_ref().as_bytes());
mac.verify_slice(signature.strip_prefix("Hmac ").unwrap_or(signature.as_bytes()))?;
// 2) Check state (check)
let order = get_order_from_db(payload.order_id).await?;
if order.status != "pending" {
return Ok(HttpResponse::BadRequest().body("Order not eligible"));
}
// 3) Use (mutation) — TOCTOU window between check and use
cancel_order_in_db(payload.order_id).await?;
Ok(HttpResponse::Ok().finish())
}
In this example, the signature is verified and the order status is checked, but the actual cancellation occurs after the check. An attacker could alter the order status between the check and the cancellation if another request or background job runs concurrently.
Secure pattern: combine check and use atomically
use actix_web::{web, HttpResponse, Result};
use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::sync::Mutex;
// A simple in-process lock to illustrate atomic check+use. In production, prefer DB-level constraints or transactions.
lazy_static::lazy_static! {
static ref ORDER_MUTEX: Mutex<()> = Mutex::new(());
}
async fn cancel_order_secure(
payload: web::Json<CancelRequest>,
headers: web::Header<actix_web::http::header::Authorization>,
) -> Result<HttpResponse> {
// 1) Verify Hmac signature first
let _signature = headers.to_string();
let mut mac = HmacSha256::new_from_slice(b"secret_key")?;
mac.update(payload.as_ref().as_bytes());
mac.verify_slice(_signature.strip_prefix("Hmac ").unwrap_or(_signature.as_bytes()))?;
// 2) Acquire lock to ensure atomic check+use
let _guard = ORDER_MUTEX.lock().unwrap();
// 3) Re-check state immediately before mutation within the lock
let order = get_order_from_db(payload.order_id).await?;
if order.status != "pending" {
return Ok(HttpResponse::BadRequest().body("Order not eligible"));
}
// 4) Use (mutation) — check and use are now effectively atomic
cancel_order_in_db(payload.order_id).await?;
Ok(HttpResponse::Ok().finish())
}
In the secure pattern, the Hmac verification remains the first integrity check, but the status check and cancellation are protected by a lock to minimize the TOCTOU window. For stronger guarantees, push the status condition into the database operation (e.g., a conditional UPDATE with a WHERE clause that checks status) so the check and use are performed within a single transaction, eliminating the window entirely.
Additionally, prefer database transactions or optimistic concurrency (e.g., version numbers) over in-process locks for distributed systems. Hmac signatures should cover all critical request fields, and handlers should avoid relying on stale reads when making authorization decisions that lead to state changes.