HIGH phishing api keysfastapihmac signatures

Phishing Api Keys in Fastapi with Hmac Signatures

Phishing API Keys in FastAPI with HMAC Signatures — how this specific combination creates or exposes the vulnerability

When FastAPI services use HMAC signatures to authenticate requests, a common misconfiguration can expose API keys to phishing-style attacks. The vulnerability arises when the server accepts the HMAC signature in an easily interceptable or spoofable location—such as a query parameter or a non-tamper-evident header—while also exposing key material through logs, error messages, or an unauthenticated endpoint that reveals integration details.

An attacker can craft a phishing site that lures a user or a downstream service to make a request to the FastAPI endpoint with a valid HMAC signature. If the signature is computed over a limited set of headers (e.g., only the payload) and the server does not enforce strict referer or origin checks, the attacker may capture the signed request through social engineering or a compromised client. Because the API key is used to generate the HMAC, exposing the key or the signed pattern effectively compromises authentication.

Additionally, if the FastAPI app provides an unauthenticated route that echoes request metadata or returns verbose error details, an attacker can use that endpoint to infer the structure of signed requests and the key’s usage pattern. This can facilitate automated phishing campaigns where attackers replay captured signed requests against the API. The risk is compounded when the HMAC implementation does not include a nonce or timestamp, allowing replay attacks even without key extraction.

Consider a FastAPI route that expects an HMAC-SHA256 signature in the X-API-Signature header, computed over the request body and a static API key stored in an environment variable. If the route does not validate the request origin and does not enforce HTTPS strictly, an attacker can trick a client into submitting a signed request to the endpoint via an image tag or script injection, potentially capturing the signature and learning how the key is used.

To illustrate a vulnerable setup, here is an example of a FastAPI route with an incomplete HMAC verification approach that can be abused in phishing scenarios:

import hmac
import hashlib
from fastapi import FastAPI, Header, HTTPException

app = FastAPI()
API_KEY = b"insecure-default-key"

def verify_hmac(body: bytes, signature: str) -> bool:
    computed = hmac.new(API_KEY, body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(computed, signature)

@app.post("/webhook")
async def webhook_endpoint(body: str, x_api_signature: str = Header(None)):
    if not x_api_signature or not verify_hmac(body.encode(), x_api_signature):
        raise HTTPException(status_code=401, detail="Invalid signature")
    return {"status": "ok"}

This example lacks timestamp or nonce validation, and the API key is hardcoded, making it susceptible to phishing and replay. The signature is also passed in a custom header that may be logged in plaintext by intermediaries, increasing exposure risk.

HMAC Signatures-Specific Remediation in FastAPI — concrete code fixes

Secure HMAC usage in FastAPI requires combining cryptographic best practices with strict request validation. The key should never be hardcoded or exposed through logs, and the signature must cover contextual elements such as timestamps or nonces to prevent replay attacks. All communication must be enforced over HTTPS, and the server should reject requests with ambiguous or missing origin headers.

Below is a hardened FastAPI implementation that includes timestamp validation, constant-time comparison, and secure key management via environment variables:

import hmac
import hashlib
import os
import time
from fastapi import FastAPI, Header, HTTPException, Request

app = FastAPI()
API_KEY = os.environ.get("API_KEY")
if not API_KEY:
    raise RuntimeError("API_KEY environment variable is required")
API_KEY = API_KEY.encode()

def verify_hmac_with_timestamp(body: bytes, signature: str, timestamp: str) -> bool:
    # Reject if timestamp is too old (e.g., 5 minutes)
    try:
        ts = int(timestamp)
    except ValueError:
        return False
    if abs(time.time() - ts) > 300:
        return False
    # Include timestamp in the signed payload
    payload = f"{body.decode()}{ts}".encode()
    computed = hmac.new(API_KEY, payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(computed, signature)

@app.post("/webhook")
async def webhook_endpoint(request: Request, x_api_signature: str = Header(None), x_api_timestamp: str = Header(None)):
    if not x_api_signature or not x_api_timestamp:
        raise HTTPException(status_code=400, detail="Missing signature or timestamp")
    body = await request.body()
    if not verify_hmac_with_timestamp(body, x_api_signature, x_api_timestamp):
        raise HTTPException(status_code=401, detail="Invalid signature or timestamp")
    return {"status": "ok"}

This approach ensures that each signature is unique per request by binding it to a timestamp, mitigating replay attacks. The key is sourced from a secure environment variable, and the comparison uses hmac.compare_digest to prevent timing attacks. For additional protection, consider including a nonce and validating the request’s origin header to reduce phishing success.

For teams managing multiple services, the middleBrick Web Dashboard or the CLI tool can be used to scan FastAPI endpoints for HMAC misconfigurations. The Pro plan adds continuous monitoring to detect when API key usage patterns suggest phishing or credential exposure, while the GitHub Action can fail builds if insecure HMAC practices are found in deployed code.

Frequently Asked Questions

How can I prevent replay attacks when using HMAC signatures in FastAPI?
Include a timestamp or nonce in the signed payload and enforce a short validity window (e.g., 5 minutes). Always use hmac.compare_digest for comparison and require HTTPS for all requests.
Is it safe to pass the HMAC signature as a query parameter in FastAPI?
No. Query parameters can be logged in server logs, browser history, and proxy logs, increasing the risk of exposure. Use headers instead and ensure they are not inadvertently logged.