Starlette CSRF Middleware

3.0.0 · active · verified Thu Apr 16

Starlette-CSRF is an active Python middleware designed for Starlette and FastAPI applications to mitigate Cross-Site Request Forgery (CSRF) attacks. It implements the Double Submit Cookie technique, providing protection by requiring a secret value to be sent in both a cookie and a request header for unsafe HTTP methods. The library is currently at version 3.0.0 and maintains a steady release cadence, with the latest major update focusing on Python version compatibility and argument handling.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to integrate `CSRFMiddleware` into a FastAPI application. A `GET` request will automatically receive a `csrftoken` cookie and expose the token in `request.state.csrftoken`. For `POST` requests, the client is expected to include this token in an `x-csrftoken` header for successful validation. The example shows how to embed this in an HTML form.

import os
import uvicorn
from fastapi import FastAPI, Request, Response, Form
from starlette.middleware import Middleware
from starlette.routing import Route
from starlette.responses import HTMLResponse
from starlette_csrf import CSRFMiddleware

# Ensure you have a strong secret key
SECRET_KEY = os.environ.get('STARLETTE_CSRF_SECRET', 'a-very-secret-key-that-you-should-change-in-production')

app = FastAPI(
    middleware=[
        Middleware(CSRFMiddleware, secret=SECRET_KEY)
    ]
)

@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
    # The CSRF token is automatically set in a cookie on GET requests
    # and can be accessed via request.state.csrftoken for templates.
    # In a real application, you'd embed this in your HTML forms.
    token = request.state.csrftoken if hasattr(request.state, 'csrftoken') else 'No token (GET request initial load)'
    return f'''
    <html>
        <head>
            <title>CSRF Test</title>
        </head>
        <body>
            <h1>Welcome!</h1>
            <p>CSRF Token in state (for display only): {token}</p>
            <form method="post" action="/submit">
                <input type="text" name="item" placeholder="Enter item">
                <!-- In a real frontend, you'd get this from a cookie or initial GET response -->
                <input type="hidden" name="x-csrftoken" value="{{request.state.csrftoken}}">
                <button type="submit">Submit</button>
            </form>
            <script>
                // For AJAX requests, you'd extract the csrftoken cookie and send it in the header
                // Example (conceptual, requires frontend JS to read cookie):
                // const csrfToken = document.cookie.split('; ').find(row => row.startsWith('csrftoken=')).split('=')[1];
                // fetch('/submit', {
                //     method: 'POST',
                //     headers: {
                //         'Content-Type': 'application/x-www-form-urlencoded',
                //         'x-csrftoken': csrfToken
                //     },
                //     body: 'item=ajax_test'
                // });
            </script>
        </body>
    </html>
    '''

@app.post("/submit")
async def submit_item(item: str = Form(...), response: Response = None):
    # The middleware automatically validates the token from the 'x-csrftoken' header
    # If validation fails, it returns a 403 Forbidden before this handler is called.
    return {"message": f"Item '{item}' received successfully!"}

if __name__ == "__main__":
    # To run: uvicorn your_app_file_name:app --reload
    # Then open http://127.0.0.1:8000
    uvicorn.run(app, host="127.0.0.1", port=8000)

view raw JSON →