HIGH padding oraclefastapidynamodb

Padding Oracle in Fastapi with Dynamodb

Padding Oracle in Fastapi with Dynamodb — how this specific combination creates or exposes the vulnerability

A padding oracle arises when an application reveals whether decrypted data is valid during an error-based side channel. In a Fastapi service that stores ciphertexts in Dynamodb and decrypts them locally, the combination of block cipher usage (e.g., AES-CBC) and informative error messages can expose this side channel. If Fastapi returns distinct errors such as “invalid padding” versus “invalid signature”, an attacker can iteratively submit modified ciphertexts stored in Dynamodb and infer validity from HTTP status codes or response content, gradually recovering plaintext without the key.

Consider a Fastapi endpoint that reads a ciphertext blob from Dynamodb, decrypts it with AES-CBC, and then validates a MAC or attempts to parse structured data. When Dynamodb returns an item, Fastapi may produce different responses depending on whether the padding is correct before MAC verification, or whether the MAC fails after padding removal. For example, a 400 response with “invalid padding” indicates a successful padding guess, whereas a 403 or 400 with “authentication failed” suggests padding is valid but integrity verification failed. This distinction allows an adaptive chosen-ciphertext attack that bypasses the lack of direct block-cipher misuse by turning application behavior and Dynamodb-stored values into an oracle.

Real-world impact aligns with OWASP API Top 10:2023 broken object level authorization (BOLA) and data exposure risks, because an attacker can recover sensitive records (PII, tokens) that reside in Dynamodb-backed resources. A concrete attack flow: an attacker enumerates IDs (BOLA/IDOR), fetches the ciphertext from Dynamodb, and launches a padding oracle attack against the decryption routine exposed by Fastapi. Even with rate limiting, carefully tuned requests can recover bytes one at a time. Instrumentation that logs padding errors or inconsistent timing further aids an attacker, so error handling must be uniform and side-channel resistant.

To illustrate the problematic pattern in Fastapi with Dynamodb, here is a simplified but realistic snippet that can lead to a padding oracle if errors are not normalized:

from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import boto3
import logging

app = FastAPI()
ddb = boto3.resource('dynamodb', region_name='us-east-1')
table = ddb.Table('secrets')
logger = logging.getLogger(__name__)

class CiphertextIn(BaseModel):
    item_id: str
    iv_b64: str

# Vulnerable: distinct errors for padding vs auth
@app.post('/decrypt')
def decrypt(data: CiphertextIn):
    item = table.get_item(Key={'id': data.item_id}).get('Item')
    if not item:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail='item not found')
    ciphertext = base64.b64decode(item['ciphertext'])
    iv = base64.b64decode(data.iv_b64)
    cipher = AES.new(b'sixteen byte key 12', AES.MODE_CBC, iv)
    try:
        padded = cipher.decrypt(ciphertext)
        plaintext = unpad(padded, AES.block_size)  # raises ValueError on bad padding
        # further processing
        return {'plaintext': plaintext.decode()}
    except ValueError as e:
        if 'padding' in str(e).lower():
            logger.warning('padding error')
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='invalid padding')
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='authentication failed')

In this example, the distinction between “invalid padding” and “authentication failed” creates an oracle. An attacker can craft ciphertexts, store them in Dynamodb, and observe HTTP status codes to perform a padding oracle attack. Even if the ciphertext is stored in Dynamodb, the server-side Fastapi logic must ensure constant-time error handling and avoid leaking padding validity to the client.

Dynamodb-Specific Remediation in Fastapi — concrete code fixes

Remediation centers on normalizing responses and ensuring that padding validation and authentication are performed in a way that does not leak information. In Fastapi, always return the same HTTP status code and generic message for invalid ciphertexts, regardless of whether the issue is padding, MAC failure, or missing item. Perform unpad and MAC verification in a unified flow that does not branch on padding correctness.

Use constant-time operations where possible and avoid logging sensitive error details. Below is a hardened Fastapi example that reads ciphertext from Dynamodb and processes it safely:

from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import boto3
import hmac
import hashlib
import secrets
import logging
import time

app = FastAPI()
ddb = boto3.resource('dynamodb', region_name='us-east-1')
table = ddb.Table('secrets')
logger = logging.getLogger(__name__)

class CiphertextIn(BaseModel):
    item_id: str
    iv_b64: str
    mac_b64: str  # HMAC-SHA256 over ciphertext

def verify_mac(key: bytes, msg: bytes, received_mac_b64: str) -> bool:
    expected = hmac.new(key, msg, hashlib.sha256).digest()
    received = base64.b64decode(received_mac_b64)
    return hmac.compare_digest(expected, received)

# Safe: uniform error handling and constant-time style practices
@app.post('/decrypt')
def decrypt(data: CiphertextIn):
    item = table.get_item(Key={'id': data.item_id}).get('Item')
    if not item:
        # Generic response to avoid leaking existence differences
        time.sleep(0.01)  # mitigate timing side channels where feasible
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail='decryption failed')
    ciphertext = base64.b64decode(item['ciphertext'])
    iv = base64.b64decode(data.iv_b64)
    received_mac = data.mac_b64
    if not verify_mac(b'sixteen byte key 12', ciphertext, received_mac):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='decryption failed')
    cipher = AES.new(b'sixteen byte key 12', AES.MODE_CBC, iv)
    padded = cipher.decrypt(ciphertext)
    try:
        # Use a constant-time unpad approach where available; here we minimize branches
        plaintext = unpad(padded, AES.block_size)  # ValueError on bad padding
    except ValueError:
        # Do not distinguish padding vs other errors
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='decryption failed')
    try:
        return {'plaintext': plaintext.decode()}
    except UnicodeDecodeError:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='decryption failed')

Key practices: validate MAC before decryption when possible, use the same status code for all failures, avoid logging raw error details that could aid an oracle, and ensure that any padding removal is performed in a manner that does not branch on secret data. For Dynamodb-specific protections, design your data model so that ciphertexts are not mutable in a way that enables an attacker to inject values used in padding oracle tests; prefer authenticated encryption (AES-GCM) where feasible to eliminate padding entirely.

These measures reduce the attack surface presented by the Fastapi+Dynamodb stack and align the implementation with secure coding guidance for handling cryptographic operations and error reporting, helping to prevent information leakage that could facilitate adaptive chosen-ciphertext attacks.

Frequently Asked Questions

Why does returning different HTTP status codes for padding errors increase risk?
Because distinct status codes or response bodies act as an oracle: an attacker can learn whether a ciphertext's padding is valid by observing the status, enabling adaptive chosen-ciphertext attacks that recover plaintext byte by byte.
Does using Dynamodb change the remediation compared to other databases?
The database choice does not alter cryptographic remediation; the focus is on uniform error handling in Fastapi, avoiding information leaks during decryption and MAC verification, regardless of where ciphertexts are stored.