Datasette Replaces CSRF Tokens with Sec-Fetch-Site Headers
Datasette PR #2689 swaps token-based CSRF for Sec-Fetch-Site header checks, eliminating hidden form tokens and skip_csrf hooks for simpler protection across forms and APIs.
Simplifying CSRF Protection Without Tokens
Traditional CSRF tokens in Datasette required adding <input type="hidden" name="csrftoken" value="{{ csrftoken() }}"> to every form in templates and manually disabling protection via the skip_csrf(datasette, scope) plugin hook for external API calls. This scattered complexity made maintenance painful. The new approach uses the browser's Sec-Fetch-Site header—set to same-origin for same-site requests and cross-site for others—to block cross-site form submissions automatically, protecting against CSRF without any tokens or per-endpoint tweaks.
This header-based method, proven secure in production, lets you build forms without extra inputs and expose APIs without CSRF exemptions, reducing boilerplate and errors.
Key Changes in PR #2689
The pull request introduces ASGI middleware inspired by Filippo Valsorda's August 2025 research essay and Go 1.25's net/http implementation:
- Replaces
asgi-csrflibrary entirely. - Deletes all
csrftoken()hidden inputs from templates. - Removes the
skip_csrfplugin hook, its docs, and tests. - Updates internals docs (https://docs.datasette.io/en/latest/internals.html#csrf-protection) and upgrade guide (https://docs.datasette.io/en/latest/upgrade_guide.html#csrf-protection) to explain header checks.
Claude Code handled 10 commits under guidance, with GPT-5.4 cross-review—AI accelerated the refactor while keeping it precise.
Why It Works and Upgrade Impact
Browsers enforce Sec-Fetch-Site reliably since Chrome 76+, covering 99%+ of users without JavaScript or tokens. It blocks malicious cross-site POSTs natively, trading zero compatibility issues for token pains. Upgrade by pulling latest Datasette: templates clean up automatically, plugins simplify sans skip_csrf. Test forms and APIs stay secure, but verify no custom skips linger.