HIGH credential stuffingfastapibasic auth

Credential Stuffing in Fastapi with Basic Auth

Credential Stuffing in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability

Credential stuffing is an automated attack in which previously breached username and password pairs are systematically replayed against an endpoint to find valid accounts. When Fastapi is configured with HTTP Basic Auth and no additional protections are in place, the API presents a clear target for these campaigns.

In Fastapi, Basic Auth is commonly implemented by providing an HTTPBasic dependency that validates a base64-encoded username:password header on each request. Because the credentials are sent with every call, an attacker who has a list of usernames and passwords can iterate through them rapidly. If the API lacks rate limiting, account lockout, or suspicious activity detection, the attacker can submit thousands of combinations per minute. Successful authentication grants access according to the permissions of the compromised account, which may include privileged roles or access to sensitive data.

Basic Auth over unencrypted HTTP is especially dangerous because credentials are easily intercepted. Even when TLS is used, other weaknesses can amplify risk: predictable or reused passwords, lack of multi-factor authentication, missing monitoring for high volumes of failed logins, and permissive CORS policies that allow browser-based leakage. Because middleBrick scans the unauthenticated attack surface, it can detect whether Basic Auth is exposed and whether observable endpoints support high request rates without throttling, both of which are prerequisites for credential stuffing.

During a scan, middleBrick’s authentication checks examine whether endpoints using Basic Auth expose timing or behavioral differences between valid and invalid credentials, which can enable password guessing. It also evaluates whether rate limiting is present and whether the API reveals informative error messages that help attackers refine their attempts. The tool cross-references these runtime observations with the OpenAPI specification, confirming whether authentication schemes are declared and whether definitions align with implementation.

To illustrate a typical Fastapi setup with Basic Auth, consider the following code. This example defines a security scheme in the OpenAPI spec and uses an HTTPBasic dependency for a protected route. Note that this code does not include protections such as rate limiting or anomaly detection, which are important defenses against credential stuffing.

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from typing import Annotated

app = FastAPI()
security = HTTPBasic()

def verify_credentials(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
    # Insecure example: plain dictionary lookup without constant-time comparison
    users = {
        "alice": "SuperSecret123",
        "bob": "Password456"
    }
    if credentials.username in users:
        if users[credentials.username] == credentials.password:
            return credentials.username
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Incorrect username or password",
        headers={"WWW-Authenticate": "Basic"}
    )

@app.get("/secure")
async def secure_data(user: Annotated[str, Depends(verify_credentials)]):
    return {"message": f"Hello, {user}"}

An attacker with this endpoint can attempt credential stuffing without needing an API key or token. middleBrick’s authentication checks can surface whether authentication responses leak information about which part of the credential pair was incorrect, and whether protections such as rate limiting are inconsistently applied across methods. By correlating spec definitions with runtime behavior, the scan highlights gaps that make credential stuffing feasible.

Basic Auth-Specific Remediation in Fastapi — concrete code fixes

Defending against credential stuffing in Fastapi with Basic Auth requires a layered approach: eliminate clear-text transmission, enforce strong passwords, add rate limiting, and avoid information leakage in error messages. The following code examples show how to implement these measures within Fastapi applications while keeping Basic Auth as the transport mechanism.

First, always use HTTPS to protect credentials in transit. Second, replace simple dictionary comparison with a constant-time check or a password hashing mechanism. Third, introduce rate limiting to slow down automated attempts. Finally, standardize error responses so they do not reveal whether a username exists.

The example below demonstrates a more resilient implementation. It uses HTTPBasic for the scheme, but adds dependencies for rate limiting and secure verification. For production use, store credentials using a secure password hashing library such as passlib with bcrypt or argon2, and integrate with a persistent user store.

from fastapi import FastAPI, Depends, HTTPException, status, Request
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from starlette.responses import JSONResponse
from typing import Annotated
from datetime import datetime, timedelta

# Simple in-memory rate store; in production use Redis or similar
request_counts = {}

def rate_limit(request: Request, max_attempts: int = 30, window_seconds: int = 60):
    client_ip = request.client.host
    now = datetime.utcnow()
    key = f"{client_ip}:{request.url.path}"
    cutoff = now - timedelta(seconds=window_seconds)
    request_counts[key] = [t for t in request_counts.get(key, []) if t > cutoff]
    if len(request_counts[key]) >= max_attempts:
        raise HTTPException(
            status_code=status.HTTP_429_TOO_MANY_REQUESTS,
            detail="Too many requests"
        )
    request_counts[key].append(now)

app = FastAPI()
security = HTTPBasic()

# Simulated hashed credentials; in practice use passlib or similar
stored_users = {
    "alice": "$argon2id$v=19$m=65536,t=3,p=4$...",  # placeholder for a real hash
}

def verify_credentials_secure(credentials: Annotated[HTTPBasicCredentials, Depends(security)], request: Request):
    rate_limit(request)
    username = credentials.username
    password_attempt = credentials.password
    # Perform constant-time comparison and hashing in real code
    if username in stored_users:
        # Example: use passlib to verify password_attempt against stored hash
        # if verify_password(password_attempt, stored_users[username]):
        #     return username
        pass
    # Generic error to avoid user enumeration
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Invalid credentials",
        headers={"WWW-Authenticate": "Basic"}
    )

@app.get("/secure")
async def secure_data(user: Annotated[str, Depends(verify_credentials_secure)]):
    return {"message": f"Hello, {user}"}

In this remediation, the rate limit function tracks request timestamps per IP and endpoint, raising a 429 response when thresholds are exceeded. Error messages are generic to prevent username enumeration. For stronger security, replace the placeholder hashing check with a well-vetted library and enforce password policies during user registration.

middleBrick can validate these improvements by scanning the API and confirming that rate limiting is observable, that authentication errors do not leak user information, and that the security scheme is correctly declared in the OpenAPI document. The tool’s findings include prioritized remediation guidance aligned with frameworks such as OWASP API Top 10 and relevant compliance controls.

Frequently Asked Questions

Why does using Basic Auth without rate limiting make credential stuffing easier?
Basic Auth sends credentials with every request and typically lacks built-in throttling. Without additional rate limiting, attackers can submit large volumes of credential pairs quickly, increasing the likelihood of successful logins.
How can I verify that my Fastapi Basic Auth implementation is not leaking information about valid usernames?
Ensure authentication error responses use the same status code and generic message for both invalid usernames and incorrect passwords. Avoid returning different messages or headers that reveal whether a username exists.