Configuring CSRF Protection in KrakenJS with the csurf Middleware
KrakenJS includes CSRF protection through the csurf middleware declared in middleware.json. Learn the correct ordering, how to render and send tokens from forms and AJAX, and the mistakes that cause 403s or silently disable protection.
23 Aug 2026, 18:10 UTC

The short answer
KrakenJS ships with CSRF protection built in: it wires up the csurf middleware for you when you declare it in middleware.json. Once it is declared after your session middleware, every route handler can call req.csrfToken() to get a per-request token, and every state-changing request (POST, PUT, DELETE, PATCH) is rejected with a 403 unless it carries a valid token. You do not need to install or configure anything beyond that declaration — but you do need to render the token into your forms and send it with AJAX calls.
This matters because cross-site request forgery lets a malicious page submit requests using your user's session cookie. The token proves the request originated from a page your server rendered, not from a third-party site.
How the pieces fit together
KrakenJS reads config/middleware.json at startup and registers each named middleware in order. Order is the critical detail: csurf depends on a session (or a cookie, depending on options) to store its secret, so it must come after the session middleware. A typical declaration looks like this:
{
"middleware": {
"session": {
"enabled": true,
"params": {
"secret": "replace-with-a-long-random-string"
}
},
"csurf": {
"enabled": true,
"params": {
"cookie": true
}
}
}
}The "cookie": true param tells csurf to store its secret in a cookie rather than the server-side session store. Either mode works; cookie mode is useful if your sessions are not backed by a persistent store. The session secret must be long and random — a guessable secret undermines both the session and the token.
Rendering and submitting the token
In any route that renders a form, pass the token to the template:
// controllers/account.js (run in your KrakenJS app, no special permissions needed)
module.exports = function (router) {
router.get('/settings', function (req, res) {
res.render('settings', { csrfToken: req.csrfToken() });
});
router.post('/settings', function (req, res) {
// If csurf let this through, the token was valid.
res.redirect('/settings');
});
};In the Dust template (KrakenJS defaults to Dust.js), include the hidden field inside the form:
<form method="post" action="/settings">
<input type="hidden" name="_csrf" value="{csrfToken}">
<!-- other fields -->
<button type="submit">Save</button>
</form>By default csurf looks for the token in req.body._csrf, the query string, or the x-csrf-token / x-xsrf-token headers. For AJAX requests, the header is the cleanest option. Render the token into a meta tag and attach it in your client-side code:
<meta name="csrf-token" content="{csrfToken}">const token = document.querySelector('meta[name="csrf-token"]').content;
fetch('/settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-csrf-token': token },
body: JSON.stringify({ theme: 'dark' })
});Note that if you send JSON bodies, you need a body-parsing middleware (such as body-parser, also declared in middleware.json) registered before csurf so the token can be read from the body — or just use the header, which avoids the ordering concern entirely.
Limits and common mistakes
Session dependency. If you register csurf before the session middleware, or omit session support entirely without using cookie mode, req.csrfToken() will throw or tokens will fail validation. Check the order in middleware.json first when you see misconfigured csrf errors.
Stateless APIs. CSRF protection assumes cookie-based authentication, because the attack relies on browsers attaching cookies automatically. If you have a pure token-based API (Authorization header, no cookies), CSRF is not the right control and csurf will mostly get in the way — exclude those routes rather than weakening protection elsewhere.
Forgetting AJAX tokens. Forms work and then the first fetch call fails with 403. This is almost always a missing header, not a server bug.
Disabling it "temporarily." Commenting out csurf to debug a 403 and shipping that change is a real-world failure mode. If you must bypass it for a health-check or webhook endpoint, mount those routes before the csurf middleware or on a separate app, and require a different authentication mechanism (such as a shared signature) on them.
Verifying it works
Two manual checks confirm the setup. First, start the app, load the rendered form, and view the page source: you should see the hidden _csrf input with a non-empty value. Second, submit the form with the field removed (browser dev tools make this easy — delete the input element, then submit). The expected result is a 403 Forbidden response with an invalid CSRF token error. Then submit normally with the field intact; the request should process as usual. If the tokenless request succeeds, the middleware is not registered, is registered after your routes, or the route is mounted outside the protected chain.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.