HIGH bleichenbacher attackflaskapi keys

Bleichenbacher Attack in Flask with Api Keys

Bleichenbacher Attack in Flask with Api Keys — how this specific combination creates or exposes the vulnerability

A Bleichenbacher attack is a cryptographic side-channel that exploits timing differences in RSA decryption to recover plaintext without the key. In Flask APIs that rely on API keys protected by RSA-based token validation, the combination of a Flask endpoint performing RSA decryption and an API key validation flow can create an oracle that a remote attacker can use to gradually reveal a valid signature or decrypt captured tokens.

Consider a Flask service that validates incoming API keys by verifying an RSA-signed JWT or PKCS#1 v1.5–style token. If the server returns different HTTP status codes or response times depending on whether a padding error occurs during RSA decryption, an attacker can iteratively send modified ciphertexts and observe responses. Over many requests, this adaptive chosen-ciphertext approach can recover the plaintext or forge a valid signature. This is the core of Bleichenbacher’s original attack against PKCS #1 v1.5 padding, later generalized to other asymmetric schemes where error handling leaks information.

When API keys are embedded inside such tokens, the attack surface expands. An attacker who can influence the token’s ciphertext—by tampering with an API key claim or observing timing differences across different keys—can learn whether a given key or padding is valid. In Flask, common triggers include using cryptography or PyCrypto with non-constant-time verification or returning 500 errors vs 401/403 distinctions that signal padding failures. Even unauthenticated endpoints that accept an API key parameter and perform RSA-based validation can act as an oracle if responses are not uniform in timing and content.

Because middleBrick tests unauthenticated attack surfaces, it can detect endpoints where RSA decryption is used in combination with API key validation and highlight timing-related anomalies or inconsistent error handling. Findings map to the Authentication and Input Validation checks, and remediation guidance is included in the scan report. Without fixes, an attacker may recover enough information to forge API keys or bypass intended access controls.

Api Keys-Specific Remediation in Flask — concrete code fixes

To mitigate Bleichenbacher-style side-channel attacks when using API keys with RSA-based validation in Flask, ensure cryptographic operations run in constant time and that error handling does not leak validation state. Below are concrete, secure patterns with working code examples.

First, prefer high-level APIs that handle padding and verification safely. When using the cryptography library, use padding.PKCS1v15 with hashes and always verify using a constant-time comparison. Do not branch on padding errors; instead, catch exceptions and return a uniform response.

from flask import Flask, request, jsonify
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.exceptions import InvalidSignature
import hmac
import secrets

app = Flask(__name__)

# Example: load public key safely
with open("public_key.pem", "rb") as f:
    PUBLIC_KEY = serialization.load_pem_public_key(f.read())

def verify_signature(data: bytes, signature: bytes, key) -> bool:
    try:
        key.verify(
            signature,
            data,
            padding.PKCS1v15(),
            hashes.SHA256(),
        )
        return True
    except InvalidSignature:
        return False
    # Do not catch other exceptions and reveal details

@app.route("/protected")
def protected():
    api_key = request.args.get("api_key", "")
    token = request.args.get("token", "")
    if not api_key or not token:
        return jsonify({"error": "missing credentials"}), 400

    # Decode token safely; do not reveal which part failed
    try:
        data = token.encode("utf-8")
        is_valid = verify_signature(data, token.encode("utf-8"), PUBLIC_KEY)
        # Constant-time comparison for API key binding
        expected = secrets.compare_digest(api_key.encode("utf-8"), b"known-safe-key")
        if is_valid and expected:
            return jsonify({"status": "ok"}), 200
    except Exception:
        # Generic error path to avoid information leakage
        pass

    return jsonify({"error": "invalid credentials"}), 403

if __name__ == "__main__":
    app.run()

Second, avoid raw PKCS#1 v1.5 where possible; prefer RSAES-OAEP, which is not malleable and does not suffer from the same Bleichenbacher-like oracles. If you must use PKCS#1 v1.5, ensure decryption errors are caught and always take the same code path with identical timing characteristics.

from cryptography.hazmat.primitives.asymmetric import rsa, padding as asym_padding
from cryptography.hazmat.primitives import hashes

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

# OAEP decryption — safer against adaptive chosen-ciphertext attacks
def decrypt_oaep(ciphertext: bytes) -> bytes:
    return private_key.decrypt(
        ciphertext,
        asym_padding.OAEP(
            mgf=asym_padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None,
        ),
    )

Third, apply defense-in-depth: rate-limit endpoints, enforce short lifetimes for API keys, and avoid returning verbose errors that indicate whether an API key, signature, or padding failed. middleBrick’s Authentication and Input Validation checks can surface endpoints where timing inconsistencies or error differentiation exist, and its remediation guidance will point to these fixes.

Frequently Asked Questions

Can a Bleichenbacher attack be used against modern API key schemes that use HTTPS?
Yes, if the API key validation involves RSA decryption with PKCS#1 v1.5 padding and the server’s error responses or timing differ between padding failures and valid decryption, HTTPS does not prevent the side-channel; the attack occurs at the cryptographic layer, not the transport layer.
Does using JWTs with HMAC eliminate Bleichenbacher risks for API keys?
Using HMAC-based JWTs removes RSA padding oracles, but you must still ensure constant-time verification and avoid branching on intermediate failures. If your API key flow falls back to RSA-based signatures or uses asymmetric crypto for validation, the same class of side-channel risks can reappear.