HIGH credential stuffingfastapihmac signatures

Credential Stuffing in Fastapi with Hmac Signatures

Credential Stuffing in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Credential stuffing is an automated attack where valid credentials from other breaches are tried against an API to gain unauthorized access. When Fastapi endpoints rely on Hmac signatures for authentication without additional protections, the attack surface shifts to the signature and replay aspects rather than the plaintext password, but the risk of unauthorized access remains.

Hmac signatures are typically computed over selected parts of a request—such as the HTTP method, path, selected headers, and a timestamp—to prove possession of a shared secret without transmitting the secret itself. In Fastapi, a common pattern is to require a client to send a signature in a custom header (e.g., X-API-Signature) and to recompute the Hmac on the server using the same algorithm and shared secret. If the server validates the signature correctly but does not bind the signature tightly to a per-request nonce or timestamp, and does not enforce strict replay and rate controls, attackers can replay captured requests or perform systematic trials with different usernames while keeping the signature valid for a window of time.

During credential stuffing, the attacker iterates over many username and password pairs. With Hmac, the attacker does not need to know the shared secret if they can reuse a valid signature for a known payload. For example, if the signature covers the request body username but not the password, an attacker might reuse a captured signature for one username while substituting another username in the body, and the server may still accept the request if the signature verification does not include the exact request content that identifies the username. Additionally, if Fastapi does not enforce strict timestamp validation or allows large clock-skew windows, attackers can replay requests within the validity period. The lack of per-request uniqueness (nonce) enables replay, and insufficient rate-limiting allows high-volume attempts without triggering defenses. These gaps mean that even with Hmac integrity, the endpoint can be abused for credential stuffing, especially when user enumeration is possible via timing differences or distinct error messages.

Compounded issues often include predictable or reused nonces, missing binding of the signature to the target resource identifier (e.g., user ID), and failure to tie the signature to the authentication context (such as session or token binding). In Fastapi, developers might mistakenly believe that Hmac alone prevents tampering, but without additional controls—such as strict one-time nonces, short timestamp windows, and application-level rate limiting—the Hmac layer primarily ensures integrity, not freshness or uniqueness, leaving the API vulnerable to credential stuffing through replay and systematic trial attacks.

Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes

To mitigate credential stuffing and replay when using Hmac signatures in Fastapi, ensure each request includes a timestamp and a cryptographically random nonce, and bind the signature to the full request content and these values. Validate timestamps tightly (for example, within 5 minutes), reject reused nonces, and enforce rate-limiting at the endpoint or IP/user level. The following example demonstrates a secure Fastapi implementation that ties Hmac verification to method, path, timestamp, nonce, and the request body, and that rejects replays.

import time
import hmac
import hashlib
import secrets
from fastapi import Fastapi, Header, HTTPException, Request
from pydantic import BaseModel

app = Fastapi()

# In production, store this securely and rotate carefully
SHARED_SECRET = b'example-shared-secret'
# Simple in-memory store for replay protection (use a distributed cache in production)
seen_nonces = set()
CLOCK_SKEW_SECONDS = 300  # 5 minutes

class LoginRequest(BaseModel):
    username: str
    password: str

def verify_hmac_signature(
    method: str,
    path: str,
    timestamp: str,
    nonce: str,
    body: bytes,
    received_signature: str
) -> bool:
    # Reject if timestamp is too old or far in the future
    try:
        req_time = int(timestamp)
    except ValueError:
        return False
    now = int(time.time())
    if abs(now - req_time) > CLOCK_SKEW_SECONDS:
        return False
    # Reject if nonce was already used
    if nonce in seen_noncs:
        return False
    # Construct the data to sign: method + path + timestamp + nonce + body
    data_to_sign = f'{method}{path}{timestamp}{nonce}'.encode() + body
    expected = hmac.new(SHARED_SECRET, data_to_sign, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_signature)

@app.post('/login')
async def login(
    request: Request,
    body: LoginRequest,
    x_api_signature: str = Header(None, alias='X-API-Signature'),
    x_api_timestamp: str = Header(None, alias='X-API-Timestamp'),
    x_api_nonce: str = Header(None, alias='X-API-Nonce')
):
    # Ensure all required headers are present
    if not all([x_api_signature, x_api_timestamp, x_api_nonce]):
        raise HTTPException(status_code=400, detail='Missing required headers')
    # Verify Hmac over method, path, timestamp, nonce, and body
    if not verify_hmac_signature(
        method=request.method,
        path=request.url.path,
        timestamp=x_api_timestamp,
        nonce=x_api_nonce,
        body=request.body(),
        received_signature=x_api_signature
    ):
        raise HTTPException(status_code=401, detail='Invalid signature')
    # Mark nonce as used to prevent replay
    seen_nonces.add(x_api_nonce)
    # Proceed with authentication logic (e.g., verify body.username and body.password)
    # This is a placeholder for actual credential validation
    return {'status': 'ok'}

Key remediation points illustrated:

  • Include timestamp and nonce in the signed payload to ensure freshness and uniqueness.
  • Enforce a tight timestamp window (e.g., 5 minutes) to limit replayability.
  • Maintain server-side tracking of used nonces to reject replays; in distributed deployments, use a shared cache with TTL slightly longer than the timestamp window.
  • Validate the signature over the full request body so that changing any parameter (including username) invalidates the signature.
  • Apply rate-limiting at the API gateway or within Fastapi to limit attempts per username or IP, reducing the impact of credential stuffing even if other controls are bypassed.

For production, replace the in-memory nonce store with a persistent, TTL-backed store, use a strong secret rotation strategy, and consider adding per-user or per-IP rate limits. These Hmac-specific controls make replay and credential stuffing impractical even if attackers obtain valid request samples.

Frequently Asked Questions

Can Hmac signatures alone prevent credential stuffing if the signature covers only the username?
No. If the signature does not cover the password and allows username substitution, attackers can reuse a valid signature with different usernames, enabling credential stuffing. Include the full request content and bind signature to the exact resource to prevent this.
What additional controls should be used with Hmac signatures to mitigate replay and credential stuffing?
Use per-request timestamps and nonces, enforce tight timestamp windows, maintain server-side nonce replay protection, apply rate-limiting per user or IP, and ensure the signature covers the full request body and method.