Cryptographic Failures in Fastapi with Basic Auth
Cryptographic Failures in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability
Basic Authentication in FastAPI transmits credentials as base64-encoded strings over HTTP. Base64 is not encryption and provides no confidentiality; it is easily reversible. When used without TLS, credentials are exposed in cleartext across the network, enabling credential theft via network sniffing. This is a cryptographic failure because the protocol lacks encryption of the authentication material.
Even when TLS is used, improper implementation can weaken protections. For example, failing to enforce HTTPS redirects or allowing both HTTP and HTTPS endpoints results in credentials being sent in cleartext on the non-HTTPS path. Additionally, storing or logging credentials from Basic Auth (e.g., in access logs) without hashing or masking can lead to sensitive data exposure. The standard library’s HTTPBasic and HTTPBasicCredentials provide a straightforward mechanism, but they do not enforce transport security by themselves.
During a middleBrick scan, endpoints using Basic Auth without enforced HTTPS are flagged under Data Exposure and Encryption checks. The scanner also tests for insecure credential handling, such as logging credentials in plaintext or failing to apply consistent strict transport security. These findings map to real-world attack patterns like credential replay and man-in-the-middle (MITM) attacks, which are common in OWASP API Top 10 categories such as Broken Object Level Authorization and Data Exposure.
Basic Auth-Specific Remediation in Fastapi — concrete code fixes
Remediation centers on enforcing HTTPS and avoiding the transmission or storage of raw credentials. Always redirect HTTP to HTTPS and ensure TLS is terminated at the edge or load balancer. Do not log or store Basic Auth credentials; if you must retain them for compatibility, store only salted hashes and avoid logging raw headers.
Below are concrete FastAPI examples that demonstrate insecure and secure approaches.
Insecure example: Basic Auth over HTTP
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import uvicorn
app = FastAPI()
security = HTTPBasic()
def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
# Insecure: no transport validation, credentials handled in plaintext
correct_username = "admin"
correct_password = "secret"
if credentials.username != correct_username or credentials.password != correct_password:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/items/")
def read_items(username: str = Depends(get_current_user)):
return {"username": username}
if __name__ == "__main__":
# WARNING: running without TLS is insecure for Basic Auth
uvicorn.run(app, host="0.0.0.0", port=80)
Secure remediation: enforce HTTPS and avoid logging credentials
from fastapi import FastAPI, Depends, HTTPException, status, Request
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
import uvicorn
app = FastAPI()
# Add HTTPS redirect middleware in production to enforce TLS
app.add_middleware(HTTPSRedirectMiddleware)
security = HTTPBasic()
# In a real deployment, validate credentials against a secure user store.
# Do not log raw credentials; mask them if necessary for debugging.
def get_current_user_secure(credentials: HTTPBasicCredentials = Depends(security)):
correct_username = "admin"
correct_password = "secret"
# Use constant-time comparison to mitigate timing attacks where feasible
if not (credentials.username == correct_username and credentials.password == correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
# Avoid logging credentials; log only masked identifiers if needed
return credentials.username
@app.get("/secure-items/")
def read_secure_items(username: str = Depends(get_current_user_secure)):
return {"username": username}
# Example of how to run with TLS (use real certs in production)
# uvicorn.run(app, host="0.0.0.0", port=443, ssl_keyfile="./key.pem", ssl_certfile="./cert.pem")
Additional recommendations: use middleware to enforce HTTPS, prefer token-based authentication (e.g., OAuth2 with Bearer tokens) where possible, and ensure your TLS configuration follows current best practices (strong ciphers, valid certificates). middleBrick’s scans can validate that HTTP-to-HTTPS redirects are in place and that no endpoints expose Basic Auth over cleartext.