Replay Attack in Actix with Jwt Tokens
Replay Attack in Actix with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A replay attack in the context of Actix web services that rely on JWT tokens occurs when an attacker intercepts a valid token and reuses it to impersonate the original client. Because JWTs are often designed for stateless authentication, a token that is valid for a long window and does not carry a nonce or per-request unique value can be replayed without additional challenges. In Actix, if endpoints only validate the signature and standard claims (exp, iss, aud) without additional protections, an attacker can capture a token— for example from logs, network traffic, or insecure client storage—and replay the same request to perform actions as the victim user or service.
JWTs in this scenario expose risk when they lack mechanisms to prevent reuse. For example, consider an Actix handler that trusts a bearer token and processes sensitive operations like changing permissions or initiating a fund transfer. If the token has a long lifetime and does not include a jti (JWT ID) claim or server-side denylist, the same token can be sent to the same endpoint multiple times. Actix middleware that only verifies the token and does not bind it to a per-request context (such as a timestamp or nonce) will accept the replayed request as legitimate.
Specific patterns increase exposure: using HTTP instead of HTTPS makes interception trivial; logging authorization headers can inadvertently store tokens; and client-side storage in local storage or insecure cookies can lead to token exfiltration. Once captured, an attacker can replay the request with identical headers and body, and if idempotency is not enforced on the server, the repeated operation may have unintended side effects. This becomes especially critical in Actix applications that expose REST or GraphQL endpoints with mutation operations, where replayed mutations can alter state or escalate privileges.
Compliance mappings highlight the relevance: OWASP API Top 10 A07:2021 (Identification and Authentication Failures) and A03:2021 (Injection) relate closely, as weak authentication schemes paired with unchecked request replay can enable privilege escalation. PCI-DSS control 8.2 and SOC2 CC6.1 also emphasize the need for mechanisms to prevent credential or token reuse. middleBrick scans can surface missing replay protections— such as absent nonce/timestamp validation and lack of token denylisting— and map findings to these frameworks.
Jwt Tokens-Specific Remediation in Actix — concrete code fixes
Remediation focuses on ensuring each JWT usage includes protections that make replay impractical or detectable. Use short-lived access tokens paired with refresh tokens, include a jti claim to uniquely identify tokens, and enforce strict timestamp validation. For stateful safety, maintain a server-side denylist or a distributed cache of recently seen tokens and reject any token already used within its validity window.
In Actix, implement middleware that checks a cache or store for token identifiers before allowing the request to proceed. Below is a concise, realistic example using the jsonwebtoken crate and actix-web with a Redis cache for denylisted jti values. This example shows token extraction, validation, and a replay check using a jti lookup.
use actix_web::{dev::ServiceRequest, Error, HttpMessage};
use actix_web_httpauth::extractors::bearer::BearerAuth;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation, TokenData};
use redis::AsyncCommands;
struct Claims {
sub: String,
exp: usize,
jti: String,
// other claims as needed
}
async fn validate_no_replay(token: &str, redis_client: &redis::Client) -> Result<(), Error> {
let mut validation = Validation::new(Algorithm::HS256);
validation.validate_exp = true;
let token_data = decode::(
token,
&DecodingKey::from_secret("your-secret".as_ref()),
&validation,
).map_err(|_| actix_web::error::ErrorUnauthorized("invalid token"))?;
let mut conn = redis_client.get_async_connection().await
.map_err(|_| actix_web::error::ErrorInternalServerError("redis error"))?;
let seen: bool = conn.get::<&str, bool>(&token_data.claims.jti).await.unwrap_or(false);
if seen {
return Err(actix_web::error::ErrorUnauthorized("token replay detected"));
}
// mark this jti as seen for the token's remaining lifetime (simplified)
let _: () = conn.set_ex(&token_data.claims.jti, "1", 300).await
.map_err(|_| actix_web::error::ErrorInternalServerError("redis error"))?;
Ok(())
}
// In your handler or guard:
async fn protected_route(
auth: BearerAuth,
redis_client: web::Data,
) -> Result {
validate_no_replay(auth.token(), &redis_client).await?;
// proceed with business logic
Ok(HttpResponse::Ok().finish())
}
Additional best practices: prefer HTTPS to prevent interception, set short exp values to limit the replay window, and use nbf and iat to enforce token freshness. For higher assurance, bind tokens to client-supplied nonces or per-request timestamps and require the server to track these values. middleBrick’s scans can verify that your OpenAPI/Swagger spec documents these protections and that runtime tests confirm the presence of nonce or timestamp checks where applicable.