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