Replay Attack in Chi with Jwt Tokens
Replay Attack in Chi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A replay attack against an API built with Chi can occur when an attacker captures a valid JWT access token and re‑uses it to replay the original authenticated request. Chi is a routing library for the OCaml ecosystem; while it does not define token validation itself, applications typically decode and verify JWTs in request handlers or middleware. If the service does not enforce replay protection—such as a nonce, one‑time use, or short token lifetime—an intercepted token can be reused to perform the same operation again, potentially leading to BOLA/IDOR or unauthorized state changes.
The vulnerability surface is larger when tokens contain high privileges, long expirations, or are transmitted over non‑TLS channels. An attacker may capture tokens via network sniffing, browser storage leaks, or server‑side logs. Because Chi routes are often composed of multiple handlers, a token validated once and passed downstream can be replayed across chained routes if each handler does not independently enforce freshness checks. Even when JWTs are properly signed, the absence of per‑request uniqueness or server‑side revocation enables replay. This aligns with common findings in OWASP API Top 10 (e.g., broken object level authorization and security misconfiguration) and can be uncovered by scanning with middleBrick, which tests unauthenticated attack surfaces and flags missing replay defenses.
middleBrick’s scans include checks for authentication weaknesses and BOLA/IDOR across runtime endpoints, which can surface replay risks when valid tokens are accepted without nonce or timestamp validation. Because replay issues stem from application design and token handling rather than the routing layer itself, middleBrick complements manual review by highlighting endpoints where tokens are accepted without adequate anti‑replay controls. The scanner also inspects OpenAPI/Swagger specs (with full $ref resolution) to see whether security schemes define token scopes and whether runtime behavior matches declared protections.
Jwt Tokens-Specific Remediation in Chi — concrete code fixes
To mitigate replay attacks in Chi applications using JWTs, implement per‑request uniqueness and strict validation. Below are concrete code examples using the jwt and cohttp libraries with Chi routes. These examples assume you validate tokens on each request and enforce short lifetimes and server‑side nonce tracking.
Example 1: Short‑lived JWT validation with per‑request nonce in Chi
open Cohttp
open Cohttp_lwt_unix
open Jwt
open Lwt.Syntax
(* Assume jwt_secret is your symmetric key and a nonce store like Memcache or Redis *)
let verify_jwt_and_nonce token stored_nonces =
let* decoded = try
return (Ok (Jwt_lib.decode_secret jwt_secret token))
with Failure msg -> return (Error msg)
in
match decoded with
| Ok claims ->
(* Check exp and nbf *) *)
let current_time = Int64.of_float (Unix.gettimeofday ()) in
let* () =
match Jwt.get_exp claims, Jwt.get_nbf claims with
| Some exp, _ when Int64.compare exp current_time < 0 -> Lwt.fail (Failure "Token expired")
| _, Some nbf when Int64.compare nbf current_time > 0 -> Lwt.fail (Failure "Token not yet valid")
| _ -> Lwt.return_unit
in
(* Replay protection: ensure nonce is unique *) *)
let* nonce = Jwt.get_string_claim "nonce" claims in
if SSet.mem nonce stored_nonces then
Lwt.fail (Failure "Replay detected: nonce already used")
else
(* Store nonce with TTL slightly longer than token lifetime *)
let* () = store_nonce_with_ttl nonce (Int64.to_int exp) in
return (Ok claims)
| Error err -> Lwt.fail (Failure err)
(* Chi route using the above verifier *)
let protected_route stored_nonces =
Router.get ("/api" // Segment) ~name:"api_action" (fun req id -
let* token = Header.get req "authorization" >= function
| Some ("Bearer " ^ t) -> Lwt.return t
| _ -> Lwt.fail (Failure "Missing bearer token")
in
let* claims = verify_jwt_and_nonce token stored_nonces in
(* Proceed with business logic, passing claims *)
Server.respond_string ~status:`OK ~body:(Printf.sprintf "OK for %s" (Jwt.string_of_claims claims))
)
Example 2: Enforcing short token lifetimes and server‑side revocation
open Jwt
open Lwt.Syntax
(* Short expiration (e.g., 5 minutes) and revocation list check *)
let verify_jwt_revocation token revocation_set =
let decoded = Jwt_lib.decode_secret jwt_secret token in
match Jwt.get_exp decoded with
| Some exp when Int64.sub exp (Int64.of_float (Unix.gettimeofday ())) < 300L -
(* If token expires in <5 minutes, reject to limit reuse window *)
Lwt.fail (Failure "Token lifetime too short for this operation")
| _ -> Lwt.return_unit
in
let* () in
if SSet.mem (Jwt.jti decoded) revocation_set then
Lwt.fail (Failure "Token revoked")
else
Lwt.return decoded
(* Integration with CoHTTP + Chi *)
let route_with_revocation revocation_set =
Router.post ("/api" / "action") (fun req -
let* token = Header.get req "authorization" >= function
| Some ("Bearer " ^ t) -> Lwt.return t
| None -> Lwt.fail (Failure "Missing authorization")
in
let* claims = verify_jwt_revocation token revocation_set in
Server.respond_string ~status:`Accepted
~body:(Printf.sprintf "Action allowed, sub: %s" (Jwt.string_of_claims claims))
)
Key remediation practices include: issuing short‑lived JWTs, embedding a per‑request nonce (e.g., UUID) and validating it server‑side, maintaining a revocation or used‑nonce store with TTL aligned to token lifetime, enforcing HTTPS to prevent token interception, and validating exp/nbf/iss/aud claims rigorously. middleBrick’s scans can help identify endpoints missing these protections by correlating spec definitions with runtime behavior, and its findings often map to OWASP API Top 10 and compliance frameworks.