HIGH pii leakagefastapihmac signatures

Pii Leakage in Fastapi with Hmac Signatures

Pii Leakage in Fastapi with Hmac Signatures — how this specific combination creates or exposes the vulnerability

In FastAPI applications that use HMAC signatures for request authentication, PII leakage can occur when signed endpoints inadvertently expose protected data in responses or logs. HMAC signatures ensure integrity and authenticity by using a shared secret to sign request components (e.g., selected headers and a timestamp). However, if the application uses the same signature scope to also guard which response fields are safe to return, a mismatch between authentication and authorization boundaries can lead to PII exposure.

Consider a FastAPI endpoint that accepts an HMAC-signed request and returns user profile information. If the signing process validates the request but the endpoint logic does not enforce field-level authorization before serialization, sensitive PII such as email, phone number, or national ID can be included in the JSON response. An attacker with a valid HMAC (e.g., obtained through insecure storage or a stolen client secret) can therefore read PII even when the endpoint lacks an access-control layer beyond signature verification.

Logging practices compound the risk. When FastAPI logs full request and response bodies for debugging, HMAC-signed endpoints that return PII can permanently expose that data in log stores. A signed request that includes a user’s email as a query parameter or in the payload may result in the PII being written to application or access logs, bypassing any runtime protections. Additionally, if the HMAC scope is too broad (covering entire request bodies rather than a minimal set of integrity-critical parameters), it can inadvertently encourage developers to place sensitive data within signed structures, increasing the blast radius of a leaked signature or secret.

The interaction with OpenAPI/Swagger spec analysis is relevant here: an automated scanner can cross-reference the declared response schema with runtime behavior. If the spec lists fields that should be restricted (such as email or ssn) but the implementation lacks corresponding serialization filters, the scan can detect a discrepancy between documented and enforced data exposure. This helps identify where HMAC-signed endpoints are unintentionally returning PII and where additional authorization or masking is required.

Hmac Signatures-Specific Remediation in Fastapi — concrete code fixes

To mitigate PII leakage when using HMAC signatures in FastAPI, implement strict separation between authentication and authorization, enforce field-level filtering before serialization, and avoid logging sensitive payloads. Below are concrete, working examples that demonstrate secure patterns.

Minimal HMAC validation without exposing PII

Validate the HMAC over a controlled set of headers and a timestamp, then return only non-sensitive fields. Do not rely on the signature to imply authorization for sensitive data.

from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.responses import JSONResponse
import hmac
import hashlib
import time
import os

app = FastAPI()

SHARED_SECRET = os.environ.get("HMAC_SECRET", "change-this-secret").encode()
ACCEPTABLE_DRIFT = 300  # seconds

def verify_hmac(request: Request):
    timestamp = request.headers.get("X-Request-Timestamp")
    received = request.headers.get("X-Signature")
    if not timestamp or not received:
        raise HTTPException(status_code=401, detail="Missing signature headers")
    # Prevent replay: reject old timestamps
    if abs(time.time() - int(timestamp)) > ACCEPTABLE_DRIFT:
        raise HTTPException(status_code=401, detail="Request expired")
    payload = f"{request.method}{request.path}{timestamp}".encode()
    expected = hmac.new(SHARED_SECRET, payload, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, received):
        raise HTTPException(status_code=401, detail="Invalid signature")

@app.get("/profile")
async def profile(request: Request, auth=Depends(verify_hmac)):
    # Return only safe, non-PII fields
    return JSONResponse({
        "user_id": 12345,
        "username": "jdoe",
        "role": "member"
    })

Explicit field selection and response masking

Define a projection that excludes PII and apply it before serialization. This prevents accidental exposure if model fields change.

from typing import Any, Dict
from fastapi import FastAPI, Request, Depends
import hmac
import hashlib
import time
import os

app = FastAPI()

SHARED_SECRET = os.environ.get("HMAC_SECRET", "change-this-secret").encode()

def verify_hmac(request: Request):
    timestamp = request.headers.get("X-Request-Timestamp")
    received = request.headers.get("X-Signature")
    if not timestamp or not received:
        raise HTTPException(status_code=401, detail="Missing signature headers")
    if abs(time.time() - int(timestamp)) > 300:
        raise HTTPException(status_code=401, detail="Request expired")
    payload = f"{request.method}{request.path}{timestamp}".encode()
    expected = hmac.new(SHARED_SECRET, payload, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, received):
        raise HTTPException(status_code=401, detail="Invalid signature")

# Simulated user model
class User:
    def __init__(self, user_id: int, username: str, email: str, ssn: str):
        self.user_id = user_id
        self.username = username
        self.email = email
        self.ssn = ssn

def safe_user_projection(user: User) -> Dict[str, Any]:
    # Explicitly exclude PII
    return {
        "user_id": user.user_id,
        "username": user.username,
        "role": "member"
    }

@app.get("/users/{user_id}")
async def get_user(user_id: int, request: Request, auth=Depends(verify_hmac)):
    # In practice, fetch from DB
    user = User(user_id=user_id, username="jdoe", email="[email protected]", ssn="123-45-6789")
    return safe_user_projection(user)

Operational and design guidance

  • Scope HMAC to integrity-critical parameters only (e.g., method, path, timestamp, selected headers). Avoid signing entire request bodies that may contain PII.
  • Apply field-level filtering or serialization views before returning responses. Do not assume signature verification implies safe data exposure.
  • Ensure logs redact or omit PII. Configure FastAPI logging to avoid writing full request/response bodies that contain sensitive information.
  • Rotate the HMAC shared secret periodically and monitor for unexpected signature patterns that may indicate compromise.
  • Combine HMAC with transport-layer encryption (TLS) to protect data in transit and reduce the risk of secret leakage via side channels.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Can HMAC signatures alone prevent PII leakage in FastAPI endpoints?
No. HMAC signatures verify request integrity and authenticity but do not enforce authorization or data minimization. You must explicitly filter PII in responses and avoid logging sensitive payloads.
How can I detect if my FastAPI endpoints are accidentally returning PII despite HMAC protection?
Use schema-aware scanning that compares your OpenAPI spec against runtime behavior. Discrepancies between declared response fields and enforced filters can indicate unintended PII exposure.