CSRF Protection

Cross-Site Request Forgery (CSRF) attacks trick authenticated users into submitting malicious requests. Nimbus provides middleware.CSRF() to protect state-changing routes by requiring a valid token with each request.

What is CSRF?

A CSRF attack occurs when a malicious site submits a form or request to your application while the user is logged in. Because the browser automatically includes cookies, the server cannot distinguish the forged request from a legitimate one. CSRF tokens solve this by requiring a secret value that only your pages know.

Setting up CSRF middleware

Create a CSRFStore and register the middleware. The built-in MemoryCSRFStore works for single-server deployments:

store := middleware.NewMemoryCSRFStore()
app.Router.Use(middleware.CSRF(store))

How it works

The middleware skips validation for safe methods (GET, HEAD, OPTIONS). For all other methods (POST, PUT, PATCH, DELETE), it checks for a valid token in:

  • The X-CSRF-Token request header
  • The csrf_token form field

If no valid token is found, the middleware responds with 403 Forbidden.

Generating tokens

Use store.Create() to generate and register a new token, or middleware.GenerateCSRFToken() for a standalone token. Pass the token to your templates or API responses:

func FormPage(c *http.Context) error {
    token := store.Create()
    return c.View("form", map[string]any{
        "csrfToken": token,
    })
}

Including CSRF token in forms

Add a hidden input field named csrf_token to your HTML forms. The value should be the token generated on the server:

<form method="POST" action="/posts">
    <input type="hidden" name="csrf_token" value="TOKEN_VALUE_HERE">
    <input type="text" name="title">
    <button type="submit">Create</button>
</form>

CSRF for AJAX requests

For JavaScript requests, include the token in the X-CSRF-Token header:

fetch("/api/posts", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-CSRF-Token": csrfToken
    },
    body: JSON.stringify({ title: "My Post" })
})

Excluding routes from CSRF

Apply CSRF middleware to specific route groups rather than globally. Routes outside the group are not checked:

// CSRF only on web routes
web := app.Router.Group("")
web.Use(middleware.CSRF(store))
web.Post("/posts", CreatePost)
web.Post("/settings", UpdateSettings)

// API routes use token auth instead — no CSRF needed
api := app.Router.Group("/api")
api.Use(auth.RequireAuth(tokenGuard, ""))
api.Post("/posts", APICreatePost)

Custom CSRF store

Implement the middleware.CSRFStore interface to use Redis, a database, or another backend for token storage:

type CSRFStore interface {
    Valid(ctx context.Context, token string) bool
}