Password Spraying in Fastapi with Basic Auth
Password Spraying in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability
Password spraying is an authentication attack where an adversary uses a small list of commonly used passwords against many accounts to avoid account lockouts. When Fastapi endpoints are protected with HTTP Basic Auth and lack effective rate limiting or suspicious login detection, the attack surface is exposed through predictable request patterns that middleBrick tests as part of its Authentication and Rate Limiting checks.
Basic Auth transmits credentials with each request using the Authorization: Basic base64(username:password) header. If the Fastapi application does not enforce per-user or global rate limits, an attacker can iterate through a password list across many usernames without triggering defensive locks. middleBrick runs parallel checks that include Rate Limiting and Authentication, flagging endpoints where identical response codes and timing allow credential guessing at scale.
In a typical Fastapi Basic Auth setup, the developer decodes the header and compares credentials directly against a user store. Without additional controls such as exponential backoff, account lockout, or CAPTCHA, a spraying campaign can proceed quietly. The attack leverages predictable endpoints (e.g., /login or resource-based routes that implicitly authenticate), and because Basic Auth does not embed additional metadata, distinguishing legitimate from malicious traffic is harder without complementary telemetry.
middleBrick’s 12 security checks operate in parallel and include Property Authorization and Input Validation, which help detect endpoints where authorization is applied inconsistently after authentication. For example, an endpoint may require valid credentials to access data but fail to ensure that authorization checks align with the authenticated identity, enabling horizontal privilege escalation when combined with spraying patterns. The scanner also flags missing or weak rate limiting that would otherwise throttle rapid, low-volume attempts characteristic of spraying.
Because middleBrick performs black-box scanning without credentials, it can evaluate unauthenticated attack surfaces and identify endpoints that inadvertently permit enumeration through timing differences or verbose error messages. Findings include severity-ranked guidance on implementing countermeasures such as rate limiting, account lockout policies, and monitoring for repeated failures across multiple usernames.
Basic Auth-Specific Remediation in Fastapi — concrete code fixes
To mitigate password spraying in Fastapi with Basic Auth, implement rate limiting, strong credential validation, and consistent response handling. The following examples show a secure Fastapi configuration with middleware-based rate limiting and proper authentication logic.
from fastapi import Fastapi, Depends, HTTPException, status, Request
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.middleware import Middleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from starlette.responses import JSONResponse
from slowapi import Limiter
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
import hashlib
import hmac
import os
app = FastAPI()
security = HTTPBasic()
# Setup rate limiter
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_middleware(
TrustedHostMiddleware, allowed_hosts=["api.example.com", "*.example.com"]
)
# In-memory store for demonstration; use a persistent, secure store in production
# Passwords should be stored as salted hashes (e.g., argon2 or bcrypt)
USERS = {
"alice": {"password_hash": hashlib.pbkdf2_hmac("sha256", b"CorrectHorseBatteryStaple", os.urandom(16), 100000).hex(), "salt": os.urandom(16).hex()},
"bob": {"password_hash": hashlib.pbkdf2_hmac("sha256", b"MyStr0ngP@ssw0rd", os.urandom(16), 100000).hex(), "salt": os.urandom(16).hex()},
}
def verify_password(stored_hash: str, salt: str, provided_password: str) -> bool:
# In practice, store and retrieve the salt per user; this example simplifies
return hmac.compare_digest(
stored_hash,
hashlib.pbkdf2_hmac("sha256", provided_password.encode("utf-8"), bytes.fromhex(salt), 100000).hex()
)
@app.exception_handler(RateLimitExceeded)
def rate_limit_handler(request: Request, exc: RateLimitExceeded):
return JSONResponse(
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
content={"detail": "Too many requests. Try again later."},
)
@app.post("/login")
@limiter.limit("5/minute")
async def login(
request: Request,
credentials: HTTPBasicCredentials = Depends(security),
):
user = USERS.get(credentials.username)
if not user:
# Return a generic response to avoid user enumeration
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Basic"},
)
if not verify_password(user["password_hash"], user["salt"], credentials.password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Basic"},
)
return {"message": "Authenticated", "username": credentials.username}
@app.get("/secure-data")
@limiter.limit("10/minute")
async def secure_data(request: Request, credentials: HTTPBasicCredentials = Depends(security)):
# Ensure the authenticated identity is correctly used for authorization
user = USERS.get(credentials.username)
if not user:
raise HTTPException(status_code=403, detail="Forbidden")
return {"data": "sensitive information for " + credentials.username}
Key remediation points:
- Rate limiting: Apply per-user or global limits (e.g., 5 attempts per minute) using a robust library like slowapi to throttle requests from single IPs or authenticated identities.
- Consistent responses: Return the same HTTP status and generic message for invalid usernames and passwords to prevent user enumeration via timing or error differences.
- Credential storage: Store passwords using salted, memory-hard hashing (e.g., Argon2, bcrypt, or PBKDF2 with sufficient iterations) rather than plaintext or weak hashes.
- Transport security: Enforce HTTPS to protect credentials in transit; Basic Auth is only safe when used over TLS.
- Monitoring and alerts: Track repeated failures across usernames and integrate with alerting to detect ongoing spraying campaigns.
middleBrick’s scans include Authentication and Rate Limiting checks, which surface endpoints where these controls are missing or inconsistent. By following the patterns above and iteratively validating with scans, teams can reduce the risk of password spraying against Fastapi services using Basic Auth.