Replay Attack in Flask with Cockroachdb
Replay Attack in Flask with Cockroachdb — how this specific combination creates or exposes the vulnerability
A replay attack occurs when an adversary intercepts a valid request or token and re-sends it to reproduce the original effect. In a Flask application using Cockroachdb as the backend, the risk arises from how authentication, session handling, and idempotent request design intersect with the database’s transactional model.
Flask itself does not provide built-in replay protection; developers must implement nonces, timestamps, or unique request identifiers. When these protections are missing or inconsistently applied, an attacker can capture a signed HTTP request—such as a fund transfer or order placement—and replay it against the same endpoint. Because Cockroachdb provides strong consistency and serializable isolation by default, a replayed request that includes a valid transaction may succeed on the database layer even if the business logic should have prevented duplication.
Cockroachdb’s transactional guarantees mean that if a Flask route performs a read–modify–write cycle without explicit idempotency keys, the database will process each replay as a new, valid transaction. For example, a payment route that decrements a balance and records a transaction row will do so on every replay, because Cockroachdb sees each request as a separate transaction. If the route uses optimistic or pessimistic locks based solely on version numbers or timestamps that do not change between replays, the second transaction may still commit depending on isolation settings and retry logic.
Additionally, if API authentication relies on static or long-lived tokens, and those tokens are included in requests that mutate state, replay becomes easier. Cockroachdb does not inherently prevent duplicate writes; it enforces constraints such as unique indexes and foreign keys. Without a server-side idempotency key stored in Cockroachdb and checked within the transaction, Flask routes may inadvertently apply the same operation multiple times, leading to financial or inventory inconsistencies.
Common OWASP API Top 10 risks related to this scenario include Broken Object Level Authorization (BOLA) and excessive data exposure when replayed requests expose sensitive paths or identifiers. SSRF or unsafe consumption patterns can also amplify the attack surface if the replayed request triggers external calls or unsafe deserialization in Cockroachdb-bound queries.
Cockroachdb-Specific Remediation in Flask — concrete code fixes
To mitigate replay attacks in Flask with Cockroachdb, implement idempotency keys, strict timestamp windows, and server-side deduplication within transactions. Below are concrete examples using the Cockroachdb Python driver with Flask.
1. Idempotency key stored in Cockroachdb
Generate a client-supplied or server-generated idempotency key and store it in a dedicated table with a unique constraint. Use it to ensure that replayed requests with the same key are ignored or returned as the original response.
import uuid
from flask import Flask, request, jsonify
import psycopg2
app = Flask(__name__)
def get_db():
return psycopg2.connect(
host="localhost",
port 26257,
user="root",
database="bank",
sslmode="require",
)
@app.route('/transfer', methods=['POST'])
def transfer():
data = request.get_json()
key = data.get('idempotency_key')
if not key:
key = str(uuid.uuid4())
amount = data['amount']
from_acct = data['from']
to_acct = data['to']
conn = get_db()
cur = conn.cursor()
try:
# Cockroachdb DDL example:
# CREATE TABLE idempotency (
# key UUID PRIMARY KEY,
# response_json JSONB,
# created_at TIMESTAMPTZ DEFAULT now()
# );
cur.execute('SELECT response_json FROM idempotency WHERE key = %s;', (key,))
existing = cur.fetchone()
if existing:
return jsonify({'status': 'duplicate', 'response': existing[0]})
cur.execute('BEGIN;')
# Deduct from sender
cur.execute('UPDATE accounts SET balance = balance - %s WHERE id = %s RETURNING balance;', (amount, from_acct))
# Add to receiver
cur.execute('UPDATE accounts SET balance = balance + %s WHERE id = %s;', (amount, to_acct))
response = {'status': 'ok', 'amount': amount, 'from': from_acct, 'to': to_acct}
# Store idempotency record atomically with the business updates
cur.execute(
'INSERT INTO idempotency (key, response_json) VALUES (%s, %s);',
(key, jsonify(response).get_json())
)
cur.execute('COMMIT;')
return jsonify(response)
except Exception as e:
cur.execute('ROLLBACK;')
return jsonify({'error': str(e)}), 500
finally:
conn.close()
2. Timestamp window with server-side validation
Require a recent timestamp (e.g., within 2 minutes) and reject requests outside the window. Store processed timestamp windows in Cockroachdb to prevent reuse across time windows.
import time
from flask import Flask, request, jsonify
import psycopg2
app = Flask(__name__)
def get_db():
return psycopg2.connect(
host="localhost",
port 26257,
user="root",
database="secureapi",
sslmode="require",
)
@app.route('/action', methods=['POST'])
def action():
data = request.get_json()
ts = data.get('timestamp')
if abs(time.time() - ts) > 120:
return jsonify({'error': 'stale timestamp'}), 400
token = data.get('nonce')
conn = get_db()
cur = conn.cursor()
try:
cur.execute('SELECT 1 FROM processed_nonces WHERE nonce = %s;', (token,))
if cur.fetchone():
return jsonify({'error': 'replay detected'}), 400
cur.execute('INSERT INTO processed_nonces (nonce) VALUES (%s);', (token,))
# proceed with business logic
return jsonify({'status': 'processed'})
finally:
conn.close()
3. Use HTTP methods and ETags for safe replay detection
For idempotent reads, rely on GET with ETag or Last-Modified headers. For writes, require an idempotency key header and store it alongside transaction state in Cockroachdb. This aligns with best practices around retries and reduces accidental duplication caused by network retransmission or client timeout handling.
Frequently Asked Questions
Does Cockroachdb prevent replay attacks by default?
Can middleBrick detect replay attack risks in Flask APIs using Cockroachdb?
middlebrick scan <url> or the GitHub Action to fail builds when risk thresholds are exceeded.