Padding Oracle in Fastapi with Basic Auth
Padding Oracle in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability
A padding oracle arises when an application reveals whether decrypted ciphertext has valid padding, enabling an attacker to iteratively decrypt encrypted data without the key. In Fastapi, using HTTP Basic Authentication does not encrypt the credentials in transit; it only encodes them in Base64. If those credentials are additionally encrypted (for example at an upstream proxy or within a token format) and the application exposes padding validation errors during decryption, the combination of Basic Auth identity and a padding oracle can be leveraged in a targeted attack.
Consider a scenario where a Fastapi service receives an Authorization header like Authorization: Basic dXNlcjpwYXNz (username:password), and the server uses a symmetric cipher (e.g., AES-CBC) to decrypt a token or cookie that was encrypted elsewhere. If the decryption logic does not use constant-time padding removal and instead returns distinct errors for invalid padding versus invalid authentication, an attacker who can observe these responses can mount a padding oracle attack. The presence of Basic Auth does not cause the oracle, but it ties the encrypted payload to a specific identity, making targeted decryption of session tokens or API keys more meaningful: the attacker knows the decrypted plaintext corresponds to a particular user captured via the Basic Auth header.
Fastapi applications that deserialize JWTs or encrypted blobs, or that handle encrypted API keys, may inadvertently expose padding errors through HTTP 400/500 responses with messages such as invalid padding, invalid character, or decryption failure. When these endpoints also require Basic Auth, the attacker can correlate the identity (username) with the decryption outcome. This specificity can amplify impact: an attacker can decrypt data bound to a particular user or service account, and may chain the recovered plaintext (e.g., a password hash or API key) with the credentials supplied in the Basic Auth header to escalate access or pivot within the system.
The 12 security checks in middleBrick operate in parallel and include Input Validation and Encryption among others. During a scan, middleBrick examines unauthenticated attack surfaces and flags scenarios where padding-related errors might be exposed, regardless of authentication mechanisms. While the scanner does not infer internal implementation, findings can highlight that error handling around decryption should be hardened to avoid leaking padding validity, especially when authentication headers like Basic Auth tie sensitive operations to specific identities.
Basic Auth-Specific Remediation in Fastapi — concrete code fixes
Remediation focuses on removing any observable difference based on padding validity and avoiding the use of error-prone manual decryption. Use well-audited libraries and ensure errors are generic. Below are two concrete Fastapi approaches: one using token-based authentication where possible, and one using encrypted payloads with constant-time handling when Basic Auth must be accepted.
Approach 1: Prefer token-based authentication (recommended)
Replace Basic Auth with bearer tokens (e.g., OAuth2 with JWTs) so credentials are not repeatedly encoded in headers and you avoid manual encryption/decryption. Use Fastapi’s OAuth2PasswordBearer and validate tokens with a library that handles padding-safe parsing.
from fastapi import Fastapi, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
import jwt # PyJWT
app = Fastapi()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
def decode_token(token: str):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except jwt.PyJWTError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
async def get_current_user(token: str = Depends(oauth2_scheme)):
return decode_token(token)
@app.get("/secure")
async def secure_endpoint(user: dict = Depends(get_current_user)):
return {"message": "ok", "user": user}
Approach 2: If you must accept Basic Auth, avoid custom encryption and use constant-time error handling
When Basic Auth is required, do not implement custom encryption/decryption that reveals padding errors. If you must process encrypted blobs, use AEAD (e.g., AES-GCM) or high-level APIs that raise uniform exceptions, and ensure HTTP error responses do not disclose padding specifics.
from fastapi import Fastapi, Depends, HTTPException, status, Header
import base64
import secrets
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
app = Fastapi()
# Example: Validate Basic Auth credentials without custom encrypted payloads
async def validate_basic_auth(authorization: str = Header(...)):
if not authorization.startswith("Basic "):
raise HTTPException(status_code=400, detail="Invalid authentication scheme")
token = authorization.split(" ", 1)[1]
decoded = base64.b64decode(token) # "user:pass"
# In real use, compare against a secure store; this is illustrative.
if decoded != b"admin:correcthorsebatterystaple":
raise HTTPException(status_code=401, detail="Invalid credentials")
return decoded
@app.get("/auth-basic")
async def basic_auth_endpoint(credentials: bytes = Depends(validate_basic_auth)):
return {"authenticated_as": credentials.decode("utf-8")}
# If you must decrypt an encrypted payload, prefer AES-GCM (AEAD) to avoid padding oracles.
def safe_decrypt(key: bytes, nonce: bytes, data: bytes, associated_data: bytes = b""):
aesgcm = AESGCM(key)
try:
return aesgcm.decrypt(nonce, data, associated_data)
except Exception:
# Always raise a generic error to avoid leaking padding or integrity details
raise ValueError("decryption failed")
Key remediation points:
- Never implement manual PKCS7 padding removal; rely on high-level cryptography libraries that handle padding safely or use authenticated encryption (AES-GCM, ChaCha20-Poly1305).
- Ensure error messages for authentication and decryption are consistent and do not differentiate between invalid credentials, invalid tokens, or padding errors.
- Use HTTPS to protect credentials in transit; Basic Auth sends credentials in an easily decoded form and should only be used over TLS.
- Consider migrating to token-based flows (OAuth2/OpenID Connect) to avoid repeated use of Basic Auth and reduce the attack surface.