HIGH cross site request forgeryfastapiapi keys

Cross Site Request Forgery in Fastapi with Api Keys

Cross Site Request Forgery in Fastapi with Api Keys — how this specific combination creates or exposes the vulnerability

Cross Site Request Forgery (CSRF) is a web security vulnerability that forces authenticated users to execute unwanted actions on a web application in which they are authenticated. In FastAPI, using only API keys for authentication does not protect against CSRF when the API is accessed from browsers via JavaScript. API keys are often stored in cookies or local storage and sent automatically by the browser with requests to the origin, making state-changing endpoints (POST, PUT, DELETE) susceptible to CSRF if no anti-CSRF mechanisms are in place.

Consider a FastAPI endpoint that transfers funds and relies on an API key passed via a cookie:

from fastapi import FastAPI, Request, Cookie

app = FastAPI()

@app.post("/transfer")
def transfer(amount: int, to: str, api_key: str = Cookie(None)):
    if not api_key:
        raise HTTPException(status_code=401, detail="Missing API key")
    # Process transfer — vulnerable to CSRF if the browser sends the cookie automatically
    return {"status": "ok"}

If a user is logged in to your site and visits a malicious site, that site can embed a form that posts to /transfer with forged parameters. Because the browser includes the API key cookie, the request appears valid. API keys sent as cookies are automatically included by the browser in cross-origin requests, which enables CSRF. In contrast, API keys sent in the Authorization header (e.g., Bearer or a custom header) are not automatically added by browsers and are therefore less vulnerable, but they can still be at risk if the CORS configuration is permissive or if the endpoint is designed to accept credentials without proper CSRF protections.

Another angle is when the OpenAPI spec (2.0/3.0/3.1) describes cookie-based API keys without explicit security schemes that discourage browser usage, and the runtime endpoints accept credentials without CSRF mitigations. middleBrick’s OpenAPI/Swagger analysis can detect whether cookie-based API key definitions exist and whether the exposed endpoints lack anti-CSRF guidance, flagging the potential for request forgery in unauthenticated scans.

CSRF in this context is not about breaking authentication but about tricking a trusted authenticated user into performing actions they did not intend. Mitigations include anti-CSRF tokens, SameSite cookie attributes, and requiring custom headers that cannot be set cross-origin by malicious sites.

Api Keys-Specific Remediation in Fastapi — concrete code fixes

To protect FastAPI endpoints while continuing to use API keys, apply defenses that prevent browsers from automatically sending credentials and enforce explicit origin checks.

1. Use the Authorization header instead of cookies

Browsers do not automatically add custom headers like Authorization in cross-origin requests triggered by malicious sites. Require API keys via the Authorization header:

from fastapi import FastAPI, Header, HTTPException

app = FastAPI()

@app.post("/transfer")
def transfer(amount: int, to: str, authorization: str = Header(None)):
    if authorization != "Bearer expected_api_key_here":
        raise HTTPException(status_code=401, detail="Invalid API key")
    return {"status": "ok"}

2. Enforce SameSite and Secure cookie attributes if cookies are required

If you must use cookies, configure SameSite=Lax or Strict and Secure to prevent cross-origin sending:

from fastapi import FastAPI, Response, Cookie

app = FastAPI()

@app.post("/set-api-key")
def set_api_key(response: Response, api_key: str):
    response.set_cookie(
        key="api_key",
        value=api_key,
        httponly=True,
        secure=True,
        samesite="strict",
    )
    return {"status": "cookie set"}

3. Add CSRF tokens for cookie-based flows

When cookie-based authentication is unavoidable, implement anti-CSRF tokens and validate them on state-changing operations:

from fastapi import FastAPI, Request, Cookie, Depends, HTTPException
from fastapi.responses import JSONResponse
import secrets

app = FastAPI()
_csrf_tokens = {}

@app.post("/csrf-token")
def get_csrf_token(response: Response, api_key: str = Cookie(None)):
    if not api_key:
        raise HTTPException(status_code=401, detail="Missing API key")
    token = secrets.token_urlsafe(32)
    _csrf_tokens[api_key] = token
    response.set_cookie(key="csrf_token", value=token, httponly=False, secure=True, samesite="strict")
    return {"csrf_token": token}

@app.post("/transfer")
def transfer(
    amount: int,
    to: str,
    api_key: str = Cookie(None),
    csrf_token: str = Cookie(None),
):
    if api_key not in _csrf_tokens or _csrf_tokens[api_key] != csrf_token:
        raise HTTPException(status_code=403, detail="Invalid CSRF token")
    return {"status": "ok"}

4. Validate Origin and Referer headers cautiously

While not a standalone fix, checking Origin or Referer can add a layer of defense. Do not rely on it alone due to potential header omission in cross-origin requests:

from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/transfer")
def transfer(
    amount: int,
    to: str,
    request: Request,
    authorization: str = Header(None),
):
    if authorization != "Bearer expected_api_key_here":
        raise HTTPException(status_code=401, detail="Invalid API key")
    origin = request.headers.get("origin")
    if origin not in (None, "https://trusted.example.com"):
        raise HTTPException(status_code=403, detail="Invalid origin")
    return {"status": "ok"}

5. Leverage CORS configuration to restrict origins

Use FastAPI’s CORSMiddleware to limit which origins can make requests:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://trusted.example.com"],
    allow_credentials=False,
    allow_methods=["POST"],
    allow_headers=["Authorization"],
)

These steps reduce the risk that an API key stored or sent by browsers can be exploited via CSRF while preserving the usability of your API.

Frequently Asked Questions

Can API keys sent in cookies be exploited via CSRF?
Yes. If API keys are stored in cookies and sent automatically by the browser, malicious sites can craft cross-origin requests that include the cookie, enabling CSRF. Use SameSite=Strict cookies, the Authorization header, or anti-CSRF tokens to mitigate this.
Does middleBrick detect CSRF risks related to cookie-based API keys?
middleBrick scans unauthenticated attack surfaces and can flag endpoints that define cookie-based API keys without clear CSRF guidance in the OpenAPI spec, helping you identify exposure.