Security
CSRF Protection
Protect your forms and APIs from Cross-Site Request Forgery attacks.
CSRF Protection
@bklarjs/csrf protects mutating requests (POST, PUT, PATCH, DELETE) using the double-submit cookie pattern. A cryptographically random token is set as a cookie and must be echoed back in a header or form field.
Installation
bun add @bklarjs/csrfnpm install @bklarjs/csrfBasic Usage
import { csrf } from "@bklarjs/csrf";
app.use(csrf());That's it. GET, HEAD, and OPTIONS requests pass through. All other methods are validated.
How It Works
- Token Generation — On any request, if no CSRF cookie exists, a 32-byte random token is generated and set as the
csrf-tokencookie. - Token Submission — The client must send the same token in:
X-CSRF-Tokenheader, OR- A
_csrffield in the request body (JSON or form-encoded)
- Validation — On mutating requests, the submitted token must match the cookie value.
Client-Side (Browser)
// Read token from cookie and include in fetch
const token = document.cookie
.split("; ")
.find((row) => row.startsWith("csrf-token="))
?.split("=")[1];
fetch("/api/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": token,
},
body: JSON.stringify({ name: "Alice" }),
});Or in a form:
<form method="POST" action="/submit">
<input type="hidden" name="_csrf" value="<%= csrfToken %>" />
<input type="text" name="name" />
</form>Configuration
app.use(csrf({
cookieName: "x-csrf-token", // Cookie name (default: csrf-token)
headerName: "x-csrf-token", // Header name (default: x-csrf-token)
fieldName: "csrf_token", // Body field name (default: _csrf)
httpOnly: false, // Must be readable by JS (default: false)
secure: true, // HTTPS only (default: false)
sameSite: "Strict", // Cookie policy (default: Strict)
}));Exposing Token to Templates
The CSRF token is available on ctx.state.csrfToken after the middleware runs:
app.get("/form", (ctx) => {
return ctx.text(`
<form method="POST" action="/submit">
<input type="hidden" name="_csrf" value="${ctx.state.csrfToken}" />
<button type="submit">Submit</button>
</form>
`, 200, { "Content-Type": "text/html" });
});Safe methods
GET, HEAD, and OPTIONS are always allowed without token validation.