HIGH replay attackflaskbearer tokens

Replay Attack in Flask with Bearer Tokens

Replay Attack in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability

A replay attack occurs when an attacker intercepts a valid request and retransmits it to reproduce the original effect. In Flask APIs that rely on Bearer Tokens for authentication, this risk is pronounced because the token itself is often the only credential presented with each request. If a Flask endpoint does not enforce strict per-request uniqueness, an attacker can capture a legitimate token and reuse it to gain unauthorized access or cause repeated side effects.

Consider a Flask route that processes a money transfer based on a Bearer token and a JSON body:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/transfer', methods=['POST'])
def transfer():
    auth = request.headers.get('Authorization')
    if not auth or not auth.startswith('Bearer '):
        return jsonify({'error': 'Unauthorized'}), 401
    token = auth.split(' ')[1]
    data = request.get_json()
    # process transfer using token as user identity
    return jsonify({'status': 'ok'})

If this endpoint lacks a nonce, timestamp, or idempotency key, an attacker who captures the Authorization header and request body can replay the same POST to transfer funds again and again. Even when tokens are rotated periodically, a captured token remains valid until expiration, enabling replay within that window.

The problem is compounded when tokens are transmitted over unencrypted channels or logged inadvertently. Insecure logging of Authorization headers in Flask apps can expose Bearer Tokens, making it easier for attackers to build replay lists. Additionally, if the API does not validate the request context (such as the combination of token, method, path, and body), the same token can be reused across different resources with different semantics.

Real-world attack patterns mirror known OAuth misconfigurations documented in the OWASP API Security Top 10, particularly 'Broken Object Level Authorization' and 'Improper Authentication'. For instance, an attacker might replay a token to a slightly altered endpoint, testing for authorization bypasses where token validity is accepted but intent is not verified. MiddleBrick’s scans include checks for missing idempotency controls and missing binding of requests to a session context, which help surface these weaknesses.

Because Bearer Tokens are static across a request’s lifetime unless explicitly rotated, replay defenses must be applied at the API layer. Without mechanisms such as one-time nonces, strict timestamp windows, or cryptographic signatures tied to the request payload, a Flask API remains vulnerable even when Bearer Tokens are used in accordance with basic syntax rules.

Bearer Tokens-Specific Remediation in Flask — concrete code fixes

To defend against replay attacks in Flask when using Bearer Tokens, you should introduce per-request uniqueness and contextual validation. Below are concrete, working code examples that you can adapt to your Flask application.

1. Enforce HTTPS and reject non-TLS requests

Ensure all API traffic is served over HTTPS to prevent token interception:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.before_request
def enforce_https():
    if not request.is_secure:
        return jsonify({'error': 'HTTPS required'}), 403

2. Validate Authorization header format consistently

Use a strict check for the Bearer token presence and avoid fragile parsing:

from flask import request

def get_bearer_token():
    auth = request.headers.get('Authorization')
    if not auth or not auth.startswith('Bearer '):
        return None
    return auth.split(' ')[1]

3. Add request-level nonce and timestamp checks

Require clients to send a nonce and a recent timestamp, and store processed nonces temporarily to prevent reuse:

import time
from flask import request, jsonify

processed_nonces = set()

def verify_request():
    token = get_bearer_token()
    if token is None:
        return False, 'Missing token'
    data = request.get_json()
    nonce = data.get('nonce') if data else None
    timestamp = data.get('timestamp') if data else None
    if not nonce or not timestamp:
        return False, 'Missing nonce or timestamp'
    # reject old requests (e.g., 5-minute window)
    if abs(time.time() - timestamp) > 300:
        return False, 'Stale timestamp'
    if nonce in processed_nonces:
        return False, 'Replay detected'
    processed_nonces.add(nonce)
    # keep the set bounded in production (e.g., prune old entries)
    return True, token

@app.route('/transfer', methods=['POST'])
def transfer():
    ok, reason = verify_request()
    if not ok:
        return jsonify({'error': reason}), 401
    # proceed with transfer logic using the validated token
    return jsonify({'status': 'ok'})

4. Bind the token to the request context

Include the token or a derived identifier in the data used to compute a request signature or include it in an idempotency key. This ensures that even if a payload is captured, it cannot be reused unchanged:

import hashlib
import json

def compute_request_id(token, data):
    payload = json.dumps(data, sort_keys=True)
    return hashlib.sha256(f'{token}:{payload}'.encode()).hexdigest()

@app.route('/transfer', methods=['POST'])
def transfer():
    token = get_bearer_token()
    if token is None:
        return jsonify({'error': 'Unauthorized'}), 401
    data = request.get_json()
    if not data:
        return jsonify({'error': 'Missing body'}), 400
    req_id = compute_request_id(token, data)
    if req_id in processed_nonces:
        return jsonify({'error': 'Duplicate request'}), 409
    processed_nonces.add(req_id)
    # process transfer
    return jsonify({'status': 'ok', 'request_id': req_id})

5. Use short token lifetimes and rotation

While not a Flask-specific code change, configuring your authentication provider to issue short-lived Bearer Tokens reduces the impact window for any captured token. Combine this with refresh token rotation and strict scope definitions to minimize lateral movement risk.

By combining HTTPS enforcement, strict header validation, nonce and timestamp windows, and request binding, you can significantly reduce the feasibility of replay attacks against Flask APIs using Bearer Tokens. These practices align with guidance reflected in security scans that check for missing idempotency and weak authentication binding.

Frequently Asked Questions

Can a replay attack happen if Bearer Tokens are transmitted over HTTPS?
Yes. HTTPS protects token confidentiality in transit, but it does not prevent an attacker from re-sending a captured, valid request. Without additional per-request uniqueness controls such as nonces or timestamps, replay attacks remain possible even over TLS.
How does middleBrick help detect replay attack risks in Flask APIs?
MiddleBrick scans the unauthenticated attack surface and checks for missing idempotency controls, missing nonce or timestamp usage, and weak authentication binding. Its findings include severity, remediation guidance, and mappings to frameworks like OWASP API Top 10 to help you prioritize fixes.