xddesigners / silverstripe-csrf-hydrate
Keep CSRF-protected forms and links working on full-page-cached Silverstripe pages: render them without a per-session SecurityID and hydrate a fresh token client-side. Includes automatic tagging of silverstripe/userforms forms.
Package info
github.com/xddesigners/silverstripe-csrf-hydrate
Type:silverstripe-vendormodule
pkg:composer/xddesigners/silverstripe-csrf-hydrate
Requires
- php: ^8.1
- silverstripe/cms: ^5 || ^6
- silverstripe/framework: ^5 || ^6
Requires (Dev)
None
Suggests
- silverstripe/userforms: User-defined forms are auto-tagged for CSRF hydration when installed
- xddesigners/silverstripe-page-cache: The full-page cache this module is designed to complement
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-17 09:08:50 UTC
README
Keep CSRF-protected forms and links working on full-page-cached pages.
When a page is full-page cached, its HTML is shared across all visitors — so it must not contain a
per-session SecurityID (CSRF token). This module lets you render such forms/links without a token
and inject a fresh one client-side: on load, a tiny script fetches the current session's token
from a small endpoint and fills it in. Designed to pair with
xddesigners/silverstripe-page-cache, but
works with any full-page cache.
- Endpoint
/csrftokenreturns the current session's token as JSON (no-store, same-origin). client/dist/csrf-hydrate.jshydrates every[data-csrf-hydrate]link/form (and re-runs after AJAX injects new DOM). No jQuery.silverstripe/userformsforms are auto-tagged when that module is installed — no template edits.
Why this is still CSRF-safe
The token endpoint only returns the current session's token to a same-origin, credentialed request; a cross-site page cannot read the response (no CORS), so it cannot steal a victim's token. A client with no JavaScript (including bots) never obtains a token, so a protected action simply cannot be performed by it — which is a useful side-effect against bot form spam and cart adds.
Requirements
- PHP
^8.1 silverstripe/framework^5 || ^6,silverstripe/cms^5 || ^6
Tested against Silverstripe CMS 6 (with silverstripe/userforms ^7). CMS 5 uses the same APIs.
Installation
composer require xddesigners/silverstripe-csrf-hydrate
Then ?flush=all. The script and a <meta name="csrf-token-endpoint"> tag are added to every content
page automatically; user-defined forms are tagged automatically.
Usage
User-defined forms (silverstripe/userforms)
Nothing to do — when userforms is installed, each UserForm is tagged with data-csrf-hydrate and
its token is refreshed client-side. Cache the page containing the form and submissions keep working.
Your own forms and links
Render the form/link without a SecurityID and add data-csrf-hydrate:
// A form: $form->setAttribute('data-csrf-hydrate', 'true'); // A GET action link that carries a token — strip it server-side, tag the link: $link = preg_replace('/([?&])SecurityID=[^&]*/', '$1', $link); // <a href="$link" data-csrf-hydrate> … </a>
The script appends ?SecurityID=<token> to tagged links and sets/creates the hidden SecurityID
input on tagged forms. Clicks/submits are blocked until hydration completes, so a token-less request
can never fire. After injecting a fragment via AJAX, call window.csrfHydrate() to hydrate it (the
built-in MutationObserver also picks up most cases automatically).
Configuration
The token endpoint is registered at /csrftoken via a Director rule. To point the script somewhere
else, change the meta tag (e.g. in a template) or override
XD\CsrfHydrate\Extensions\CsrfHydrateControllerExtension.
License
BSD-3-Clause. See LICENSE.