Metadata-Version: 2.4
Name: modern-csrf
Version: 2026.10.20
Summary: Modern anti-CSRF middleware
License-Expression: MIT
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: asgi
Requires-Dist: asgiref>=3.10.0; extra == "asgi"
Dynamic: license-file

# Block Cross-Origin Requests

This project implements the modern [Cross-Site Request Forgery (CSRF)][csrf] defenses
as [described][valsorda] by Filippo Valsorda.
This is the [Fetch metadata approach][mdn-csrf-fetch] described on MDN.
Unsafe cross-origin requests are rejected with a 403 Forbidden status by default.

[csrf]: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF
[valsorda]: https://words.filippo.io/csrf/
[mdn-csrf-fetch]: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF#fetch_metadata

This approach protects users of browsers less than a few years old
(and anyone using an older browser than that likely has worse problems to worry about).
This approach is useful
because it can be cleanly implemented as a middleware
without requiring close coordination with the application.
That doesn't mean that you can't use CSRF tokens too.

## How it works

The specific checks performed are:

1.  Allow requests with one of the safe HTTP methods: `GET`, `HEAD`, or `OPTIONS`.
1.  Allow requests from trusted origins.
1.  If the `Sec-Fetch-Site` header is set,
    allow requests with `Sec-Fetch-Site` set to `same-origin` or `none`, and
    reject other requests.
1.  Allow requests with neither of the `Sec-Fetch-Site` or `Origin` headers set.
1.  If the `Origin` and `Host` headers are both set,
    allow requests where the host-and-port portion of the origin matches the host.
1.  Reject any remaining requests.

## Things to watch out for

In order for your application to be protected by this middleware,
you *must not* change the application state
in response to a request with one of the ["safe" methods][safe]
(`GET`, `HEAD`, `OPTIONS`).
Note that this is not an additional requirement because
there are other places within the HTTP stack
where this assumption already exists.
For example,
requests with these methods may be automatically retried
without user intervention.

[safe]: https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP

For this to work well,
your application should be served from a [trustworthy origin][trustworthy],
i.e. over HTTPS or via localhost.
There may be false rejections if you do not use a trustworthy origin.

[trustworthy]: https://www.w3.org/TR/secure-contexts/#is-origin-trustworthy

## Protecting your application

Two versions of the middleware are provided:
a [Web Server Gateway Interface (WSGI)][wsgi] middleware
`modern_csrf.wsgi.BlockCrossOriginRequests`,
and an [Asynchronous Server Gateway Interface (ASGI)][asgi] middleware,
`modern_csrf.asgi.BlockCrossOriginRequests`.
Both versions have the same basic shape:
the middleware is constructed with the next application as the first argument,
and can be configured by setting keyword arguments.
The available configuration parameters are:

*   `trusted_origins`:
    A collection of origins from which requests are always allowed.
    An "origin" is specified as `scheme://host[:port]` string
    and must match *exactly*, including case.
    
    Default: `()` (i.e. no trusted origins)
    
    Example: `["https://example.com", "https://example.net:1234"]`
    
*   `reject_handler`:
    An application used to reject requests that should be rejected.
    
    The default value returns a 403 Forbidden status with a fixed message.
    
*   `log_rejected_requests`:
    Should detailed information about rejected requests be logged?
    For WSGI, the message is written to the WSGI error stream.
    For ASGI, the message is logged via the standard library logging infrastructure.
    
    Default: `False`
    
    Example: `True`
    
[wsgi]: https://peps.python.org/pep-3333/
[asgi]: https://asgi.readthedocs.io/en/latest/
