Bleichenbacher Attack in Flask (Python)
Bleichenbacher Attack in Flask with Python — how this specific combination creates or exposes the vulnerability
The Bleichenbacher attack (CVE-1998-0001) exploits padding oracle vulnerabilities in RSA PKCS#1 v1.5 decryption, allowing an attacker to decrypt ciphertexts or forge signatures by observing whether a server returns distinct error messages for invalid padding versus other decryption failures. In Flask applications using Python, this vulnerability commonly arises when developers implement custom RSA decryption logic or misconfigure TLS termination, especially in legacy systems or when using outdated cryptography libraries.
Flask itself does not handle TLS; the attack surface depends on the underlying WSGI server (e.g., Gunicorn, uWSGI) or reverse proxy (e.g., Nginx, Apache) handling HTTPS. However, if a Flask endpoint decrypts RSA-encrypted data — such as in custom authentication tokens, legacy SSO integrations, or improperly implemented JWT alternatives — and uses Python’s cryptography libraries without proper safeguards, it may become vulnerable. For example, using Crypto.Cipher.PKCS1_v1_5 from PyCryptodome and checking padding validity via exception handling can leak timing or error-based side channels.
An attacker can send thousands of modified ciphertexts to a Flask endpoint that decrypts user-supplied RSA data (e.g., a login token endpoint) and observe whether the server responds with a 'padding error' versus a 'decryption error' or generic failure. Over time, this allows the attacker to decrypt valid ciphertexts without the private key. The risk increases if the Flask app returns distinct HTTP status codes or error messages for padding failures, which can be fingerprinted.
This vulnerability is not inherent to Flask but emerges from how Python cryptography is used within Flask views. middleBrick detects such risks by scanning for unauthenticated endpoints accepting RSA-encrypted input and analyzing responses for error differentiation — part of its Input Validation and Encryption security checks. It does not decrypt data but identifies behavioral patterns consistent with padding oracles.
Python-Specific Remediation in Flask — concrete code fixes
To mitigate Bleichenbacher vulnerabilities in Flask applications, avoid using RSA PKCS#1 v1.5 for decryption entirely. Instead, use RSA-OAEP, which is provably secure against chosen-ciphertext attacks. If legacy systems require PKCS#1 v1.5, ensure constant-time error handling and uniform error responses to prevent oracle leakage.
The following code demonstrates a vulnerable Flask endpoint using PKCS#1 v1.5 decryption with error-based oracle leakage:
from flask import Flask, request, jsonify
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
app = Flask(__name__)
# Load private key (in practice, load securely)
private_key = RSA.generate(2048)
cipher = PKCS1_v1_5.new(private_key)
@app.route('/decrypt', methods=['POST'])
def decrypt():
encrypted_data = request.data
try:
decrypted = cipher.decrypt(encrypted_data, b'padding_error')
if decrypted == b'padding_error':
# This comparison leaks via timing or error handling
return jsonify({'error': 'Invalid padding'}), 400
return jsonify({'data': decrypted.decode()})
except ValueError as e:
# Distinct error message for decryption failure
return jsonify({'error': 'Decryption failed'}), 400
if __name__ == '__main__':
app.run(debug=True)
This code is vulnerable because it returns different error messages ('Invalid padding' vs 'Decryption failed') based on the outcome of padding checks, enabling an oracle attack.
The fixed version uses RSA-OAEP and ensures uniform error responses:
from flask import Flask, request, jsonify
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
app = Flask(__name__)
private_key = RSA.generate(2048)
cipher = PKCS1_OAEP.new(private_key)
@app.route('/decrypt', methods=['POST'])
def decrypt():
encrypted_data = request.data
try:
decrypted = cipher.decrypt(encrypted_data)
return jsonify({'data': decrypted.decode()})
except (ValueError, TypeError):
# Uniform error response for any decryption failure
return jsonify({'error': 'Invalid input'}), 400
if __name__ == '__main__':
app.run(debug=True)
Key changes:
- Replaced PKCS1_v1_5 with PKCS1_OAEP, which is resistant to Bleichenbacher attacks.
- Removed padding-specific error checks; all decryption failures return the same generic error message and HTTP status code.
- Avoided distinguishing between padding errors and other failures in responses or logs.
Additionally, disable debug mode in production (debug=False) to prevent leakage of stack traces. Use middleware or WSGI server configurations to ensure uniform error pages. middleBrick’s Encryption and Input Validation checks would flag the use of PKCS#1 v1.5 and inconsistent error handling as high-risk findings.