Skip to content

Releases: vshn/capjs-server

v0.2.0

03 Mar 15:46

Choose a tag to compare

Replay Protection

Challenges can now only be redeemed once per server process, preventing attackers from replaying solved challenges to stockpile verification tokens.

New features

  • NonceStore Protocol — pluggable interface for tracking used challenge nonces
  • MemoryNonceStore — thread-safe in-memory default with automatic expiry (no new dependencies)
  • nonce_store parameter on CapServer — pass a custom store (e.g. Redis) for strict single-use across replicas
  • CAP_NONCE_STORE Django setting — configure nonce store via Django settings

How it works

After verifying a challenge token's signature, redeem() now checks whether the nonce has been seen before. If it has, the request is rejected. Nonces are tracked with TTLs matching the challenge expiry, so memory stays bounded.

For single-process deployments, this works out of the box. For multi-replica deployments (e.g. Kubernetes), a challenge can be redeemed at most once per replica. For strict single-use across replicas, plug in a shared store:

from capjs_server import CapServer

class RedisNonceStore:
    def __init__(self, redis_client):
        self.redis = redis_client

    def mark_used(self, nonce: str, ttl_seconds: float) -> bool:
        return self.redis.set(f"cap:nonce:{nonce}", 1, nx=True, ex=int(ttl_seconds) + 1)

cap = CapServer(secret_key="...", nonce_store=RedisNonceStore(redis_client))

Full changelog

v0.1.0...v0.2.0