Replay Attack in Fastapi with Cockroachdb
Replay Attack in Fastapi with Cockroachdb — how this specific combination creates or exposes the vulnerability
A replay attack occurs when an attacker intercepts a valid request or token and retransmits it to impersonate a legitimate user. In a Fastapi service using Cockroachdb as the backend store, the risk arises from how authentication material or transactional state is handled between the API layer and the database.
Fastapi does not inherently protect against replay unless the developer explicitly adds nonces, timestamps, or one-time-use markers to requests. If endpoints rely only on static tokens or session identifiers stored in Cockroachdb without freshness checks, an intercepted credential can be reused. Cockroachdb’s strong consistency and serializable isolation help preserve data correctness, but they do not prevent replay at the API boundary. A common pattern is to store a per-user nonce or timestamp in Cockroachdb and validate it on each request; if this check is omitted or performed inconsistently across services, replay becomes feasible.
Another exposure point is idempotency keys used to deduplicate requests. When idempotency is implemented by storing a key in Cockroachdb and returning the same response on repeat submissions, an attacker can replay the request with the same key to trigger the same side effects if the validation does not include strict uniqueness constraints and short TTLs. Misconfigured unique constraints or missing TTLs in Cockroachdb can allow duplicate entries, enabling replay. Additionally, if Fastapi endpoints accept mutable query parameters or headers that influence authorization without cryptographic protection, an attacker can rotate trivial parameters (such as timestamps or counters) while keeping the signature valid, especially when signature verification is done asynchronously against Cockroachdb state.
Consider an endpoint that accepts a payment request with an amount and a client-supplied nonce stored in Cockroachdb. If the server does not verify that the nonce has not been used within a short window, an attacker can replay the same payment request. Since Cockroachdb serializable transactions ensure that reads and writes are consistent, the duplicate nonce might be accepted unless explicit uniqueness checks and row-level constraints are enforced. The combination of Fastapi’s flexible routing and Cockroachdb’s transactional guarantees can inadvertently encourage developers to assume safety without implementing anti-replay controls at the edge.
Cockroachdb-Specific Remediation in Fastapi — concrete code fixes
To mitigate replay in a Fastapi and Cockroachdb stack, enforce freshness and uniqueness at the database and application layers. Use one-time tokens, monotonic counters, or time-bound nonces stored in Cockroachdb with uniqueness constraints and short TTLs. Validate these values within serializable transactions to ensure atomic check-and-set behavior.
Example 1: Nonce validation with uniqueness constraint
import asyncio
from fastapi import Fastapi, HTTPException, Depends
from pydantic import BaseModel
from cockroachdb import AsyncClient
app = Fastapi()
class NonceRequest(BaseModel):
user_id: str
nonce: str
amount: int
async def get_db():
# returns an async Cockroachdb client configured for your cluster
return AsyncClient(connection_string="postgresql://user:password@host:26257/db?sslmode=require")
@app.post("/pay")
async def pay(request: NonceRequest, db=Depends(get_db)):
async with db.transaction() as tx:
# Ensure the nonce has not been used; enforce uniqueness in the table schema
row = await tx.fetchrow(
"SELECT id FROM nonces WHERE user_id=$1 AND nonce=$2 FOR UPDATE",
request.user_id,
request.nonce
)
if row is not None:
raise HTTPException(status_code=400, detail="Replay detected: nonce already used")
# Record nonce with a short TTL via scheduled cleanup or USING INDEX
await tx.execute(
"INSERT INTO nonces (user_id, nonce, created_at) VALUES ($1, $2, now()) ON CONFLICT (user_id, nonce) DO NOTHING",
request.user_id,
request.nonce
)
# Proceed with payment logic
await tx.execute(
"UPDATE accounts SET balance = balance - $1 WHERE user_id = $2",
request.amount,
request.user_id
)
return {"status": "ok"}
Key points: the table nonces should define a uniqueness constraint on (user_id, nonce), and the transaction uses FOR UPDATE to lock rows and prevent race conditions under serializable isolation. The ON CONFLICT DO NOTHING pattern ensures idempotent insertion without duplicates; if a duplicate is attempted, the application detects it and rejects the request.
Example 2: Idempotency key with TTL and uniqueness
import asyncio
from fastapi import Fastapi, HTTPException, Depends
from cockroachdb import AsyncClient
from datetime import datetime, timedelta
app = Fastapi()
async def get_db():
return AsyncClient(connection_string="postgresql://user:password@host:26257/db?sslmode=require")
@app.post("/charge")
async def charge(idempotency_key: str, user_id: str, amount: int, db=Depends(get_db)):
ttl = (datetime.utcnow() + timedelta(hours=1)).isoformat()
async with db.transaction() as tx:
# Use a uniqueness constraint on idempotency_key; enforce TTL via periodic cleanup or USING INDEX
row = await tx.fetchrow(
"SELECT response FROM idempotency WHERE key = $1 FOR UPDATE",
idempotency_key
)
if row is not None:
return {"cached_response": row["response"]}
# Perform the charge (placeholder)
await tx.execute(
"INSERT INTO idempotency (key, user_id, created_at, expires_at) VALUES ($1, $2, now(), $3)",
idempotency_key,
user_id,
ttl
)
# Actual business logic here
return {"status": "charged"}
Define the idempotency table with a uniqueness constraint on key and an index on expires_at for efficient TTL cleanup. By checking existence within a serializable transaction and storing a response, you prevent duplicate processing while ensuring replayed requests return the cached result rather than causing side effects.
Schema and operational guidance
Create tables with explicit constraints:
CREATE TABLE nonces (
user_id STRING NOT NULL,
nonce STRING NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (user_id, nonce)
);
CREATE TABLE idempotency (
key STRING NOT NULL UNIQUE,
user_id STRING NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
response JSONB
);
Use Cockroachdb’s built-in capabilities to enforce uniqueness and TTL-based retention. In Fastapi, always perform nonce and idempotency checks inside database transactions to preserve atomicity. Combine these checks with transport-layer security and short-lived session tokens to reduce the attack surface across the stack.