| name | padding-oracle-anti-pattern |
| description | Security anti-pattern for padding oracle vulnerabilities (CWE-649). Use when generating or reviewing code that decrypts CBC-mode ciphertext, handles decryption errors, or returns different errors for padding vs other failures. Detects error message oracles. |
Padding Oracle Anti-Pattern
Severity: High
Summary
Applications leak padding correctness during decryption through different error messages ("Invalid Padding" vs. "Decryption Failed") or timing differences. Attackers manipulate ciphertext and observe responses to decrypt entire messages byte-by-byte without knowing the key, breaking confidentiality.
The Anti-Pattern
The anti-pattern is using CBC mode and returning different responses based on decryption error type.
BAD Code Example
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from flask import request
KEY = b'sixteen byte key'
@app.route("/decrypt")
def decrypt_data():
encrypted_data = request.args.get('data').decode('hex')
iv = encrypted_data[:16]
ciphertext = encrypted_data[16:]
cipher = Cipher(algorithms.AES(KEY), modes.CBC(iv))
decryptor = cipher.decryptor()
try:
decrypted_padded = decryptor.update(ciphertext) + decryptor.finalize()
unpadder = padding.PKCS7(128).unpadder()
unpadded_data = unpadder.update(decrypted_padded) + unpadder.finalize()
return "Decryption successful!", 200
except ValueError as e:
if "padding" in str(e).lower():
return "Error: Invalid padding.", 400
else:
return "Error: Decryption failed.", 500
GOOD Code Example
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from flask import request
KEY = AESGCM.generate_key(bit_length=128)
def encrypt_gcm(data):
aesgcm = AESGCM(KEY)
nonce = os.urandom(12)
ciphertext = aesgcm.encrypt(nonce, data, None)
return nonce + ciphertext
@app.route("/decrypt/secure")
def decrypt_data_secure():
encrypted_data = request.args.get('data').decode('hex')
nonce = encrypted_data[:12]
ciphertext_with_tag = encrypted_data[12:]
aesgcm = AESGCM(KEY)
try:
decrypted_data = aesgcm.decrypt(nonce, ciphertext_with_tag, None)
return "Decryption successful!", 200
except InvalidTag:
return "Error: Decryption failed or data is corrupt.", 400
Detection
- Review decryption code: Look for any code that decrypts data using CBC mode.
- Examine error handling: Check the
try...except blocks around decryption logic. Does the code catch different exceptions (e.g., PaddingError, CryptoError) and return different HTTP responses, status codes, or error messages for each?
- Look for timing differences: In some rare cases, the oracle can be a timing side channel, where valid padding checks take slightly longer than invalid ones. This is much harder to detect via code review.
- Perform active testing: Use a tool like
padbuster to actively test an endpoint for a padding oracle vulnerability.
Prevention
Related Security Patterns & Anti-Patterns
References