Replay Attack in Actix with Bearer Tokens
Replay Attack in Actix with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A replay attack in Actix using Bearer Tokens occurs when an intercepted authentication token is reused by an attacker to impersonate a legitimate user or service. Because Bearer Tokens are static credentials transmitted with each request, an attacker who captures a valid token can replay the original HTTP request—including method, path, headers, and body—to perform unauthorized actions without needing to know the secret that generated the token.
This risk is especially relevant for unauthenticated or insufficiently protected endpoints scanned by tools like middleBrick, which tests the unauthenticated attack surface. For example, an endpoint such as POST /api/transfer that only validates the presence of a Bearer Token is vulnerable if it does not ensure request uniqueness. An attacker can capture a legitimate transfer request from a victim’s browser or network traffic and replay it later to initiate fraudulent transfers or modify resources.
Replay attacks are not inherently tied to token format; they exploit the lack of request-level anti-replay controls. In Actix, this often means missing or misconfigured protections such as nonce usage, timestamp validation, or idempotency keys. Without these, a token that appears valid will be accepted on every replay, and the server cannot distinguish between the original request and a malicious repeat.
Consider a scenario where an API relies solely on HTTPS for transport security. If an attacker gains access to network traffic—via compromised Wi‑Fi, a malicious proxy, or insecure logging—they can extract the Bearer Token from captured requests. The attacker can then use tools like curl or custom scripts to replay recorded requests against the Actix service. middleBrick’s unauthenticated scan may surface such endpoints by testing whether requests with valid-looking tokens can be repeated with identical parameters and headers, highlighting the absence of replay defenses.
Because Bearer Tokens are often long-lived for convenience, the window of exposure is extended. Replayed requests may succeed for a significant duration, especially if token rotation is infrequent or absent. This amplifies the impact of token leakage and increases the severity of findings reported by security scanners that include LLM/AI security checks, where prompt-injection-like patterns may simulate token misuse scenarios.
In summary, the combination of Actix endpoints, Bearer Tokens, and missing request uniqueness validation creates a pathway for replay attacks. The risk is not in the token format itself but in how the service consumes and validates each request. Security checks that verify the presence of nonce or timestamp headers, enforce short token lifetimes, and correlate requests with server-side state are essential to mitigate this threat.
Bearer Tokens-Specific Remediation in Actix — concrete code fixes
Remediation focuses on ensuring that each request is unique and cannot be safely replayed, even if a Bearer Token is valid. Below are concrete Actix-web patterns that introduce replay protection through timestamps, nonces, and idempotency keys.
1. Timestamp-based replay protection
Require a timestamp header and reject requests with timestamps outside an acceptable window. This ensures that even if a token is captured, the request cannot be reused after the window expires.
use actix_web::{web, HttpRequest, HttpResponse, Error};
use std::time::{SystemTime, UNIX_EPOCH};
const REPLAY_WINDOW_SECS: i64 = 300; // 5 minutes
async fn transfer_handler(
req: HttpRequest,
body: web::Json,
) -> Result {
let token = req.headers().get("Authorization")
.and_then(|h| h.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "))
.ok_or_else(|| actix_web::error::ErrorUnauthorized("Missing Bearer Token"))?;
let timestamp_header = req.headers().get("X-Request-Timestamp")
.and_then(|h| h.to_str().ok())
.ok_or_else(|| actix_web::error::ErrorBadRequest("Missing timestamp"))?;
let timestamp: i64 = timestamp_header.parse().map_err(|_| actix_web::error::ErrorBadRequest("Invalid timestamp"))?;
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
if (now - timestamp).abs() > REPLAY_WINDOW_SECS {
return Err(actix_web::error::ErrorBadRequest("Request timestamp outside allowed window"));
}
// Validate token and process transfer...
Ok(HttpResponse::Ok().json("Transfer processed"))
}
2. Nonce-based replay protection
Use a client-supplied nonce that is stored server-side for the duration of the token’s validity. Subsequent requests with the same nonce are rejected.
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
struct AppState {
used_nonces: Mutex,
data: web::Data<Arc<AppState>>,
) -> Result<HttpResponse, Error> {
let nonce = req.headers().get("X-Nonce")
.and_then(|h| h.to_str().ok())
.ok_or_else(|| actix_web::error::ErrorBadRequest("Missing nonce"))?;
let mut nonces = data.used_nonces.lock().unwrap();
if nonces.contains(nonce) {
return Err(actix_web::error::ErrorBadRequest("Replay detected: nonce already used"));
}
nonces.insert(nonce.to_string());
// Validate Bearer Token and process transfer...
Ok(HttpResponse::Ok().json("Transfer processed"))
}
3. Idempotency key pattern
Require clients to provide an idempotency key for state-changing operations. Store processed keys and responses to safely reject duplicates without reapplying side effects.
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
struct IdempotencyStore {
keys: Mutex<HashMap<String, HttpResponse>>,
}
async fn idempotent_transfer(
req: HttpRequest,
body: web::Json<TransferBody>,
store: web::Data<Arc<IdempotencyStore>>,
) -> Result<HttpResponse, Error> {
let key = req.headers().get("Idempotency-Key")
.and_then(|h| h.to_str().ok())
.ok_or_else(|| actix_web::error::ErrorBadRequest("Missing idempotency key"))?;
let mut keys = store.keys.lock().unwrap();
if let Some(resp) = keys.get(key) {
return Ok(resp.clone());
}
// Process transfer with Bearer Token validation...
let response = HttpResponse::Ok().json("Transfer processed");
keys.insert(key.to_string(), response.clone());
Ok(response)
}
In addition to these code-level fixes, operational practices matter: rotate Bearer Tokens regularly, prefer short-lived tokens with refresh mechanisms, and combine transport security (TLS) with application-level replay defenses. middleBrick’s scans, including its unauthenticated checks and optional authenticated scans, can help verify that these protections are present and correctly enforced.