CSRF protection on the login and URL-generator forms (closes #93) #117

Open
clawbot wants to merge 2 commits from issue-93-csrf into next
Collaborator

Adds CSRF protection to the two cookie-authenticated form posts in
#93POST / (login) and POST /generate. Uses github.com/gorilla/csrf v1.7.3, the recorded default in
GO_PACKAGE_DEFAULTS.md (Sessions / CSRF); it supplies crypto/rand
generation and constant-time comparison.

What changed:

  • New middleware in internal/handlers/csrf.go, wired as a chi group over
    GET /, POST /, and POST /generate in routes.go.
  • Token key derived from signing_key with its own HKDF salt
    (seal.DeriveKey): reuses no session or encrypted-URL key material,
    needs no new config, survives restarts.
  • The token cookie is independent of the session cookie, so it also covers
    login CSRF, where no session exists yet.
  • Hidden token field rendered into login.html and generator.html;
    render helpers now take the request.

What a reader would trip over:

  • In debug mode requests are marked plaintext (no Secure cookie, no
    Referer check) so local HTTP works. In production, behind the
    TLS-terminating proxy, the library enforces its https Referer origin
    check — the proxy must preserve the browser's Host and Referer, or
    posts are rejected.

Disclosures:

  • One //nolint:gosec on the test's gorilla.csrf.Token field-name
    constant (G101 false positive; upstream marks its own copy the same).
  • Gate: make lint (golangci-lint v2.12.2) 0 issues and make fmt-check
    clean on this tree, and full make test green just prior to five
    behaviour-preserving edits. The combined make check and docker build --target lint could not finish — the shared host was OOM-killing every
    build past the background handoff.

model: claude-opus-4-8

Adds CSRF protection to the two cookie-authenticated form posts in https://git.eeqj.de/sneak/pixa/issues/93 — `POST /` (login) and `POST /generate`. Uses `github.com/gorilla/csrf` v1.7.3, the recorded default in `GO_PACKAGE_DEFAULTS.md` (Sessions / CSRF); it supplies `crypto/rand` generation and constant-time comparison. What changed: - New middleware in `internal/handlers/csrf.go`, wired as a chi group over `GET /`, `POST /`, and `POST /generate` in `routes.go`. - Token key derived from `signing_key` with its own HKDF salt (`seal.DeriveKey`): reuses no session or encrypted-URL key material, needs no new config, survives restarts. - The token cookie is independent of the session cookie, so it also covers login CSRF, where no session exists yet. - Hidden token field rendered into `login.html` and `generator.html`; render helpers now take the request. What a reader would trip over: - In `debug` mode requests are marked plaintext (no `Secure` cookie, no Referer check) so local HTTP works. In production, behind the TLS-terminating proxy, the library enforces its https Referer origin check — the proxy must preserve the browser's `Host` and `Referer`, or posts are rejected. Disclosures: - One `//nolint:gosec` on the test's `gorilla.csrf.Token` field-name constant (G101 false positive; upstream marks its own copy the same). - Gate: `make lint` (golangci-lint v2.12.2) 0 issues and `make fmt-check` clean on this tree, and full `make test` green just prior to five behaviour-preserving edits. The combined `make check` and `docker build --target lint` could not finish — the shared host was OOM-killing every build past the background handoff. model: claude-opus-4-8
clawbot added the needs-review label 2026-09-21 10:02:00 +02:00
clawbot self-assigned this 2026-09-21 10:02:00 +02:00
clawbot added 2 commits 2026-09-21 14:57:25 +02:00
Failing tests (TDD) for the two cookie-authenticated HTML form posts:
a POST without a CSRF token is rejected, a token that does not match the
request's CSRF cookie is rejected, and a matching cookie+token succeeds.
Login CSRF is covered specifically: the POST / cases carry no session,
so protection rests on a token bound to a pre-session cookie.

These reference production symbols not yet added (newCSRFProtect,
Handlers.CSRF, the csrfProtect field), so the package does not build
until the implementation lands.

model: claude-opus-4-8
Both cookie-authenticated HTML form posts (POST / and POST /generate)
now require a CSRF token via gorilla/csrf, the recorded default in
GO_PACKAGE_DEFAULTS.md. The token cookie is independent of the session
cookie, so it also covers the login POST, where no session exists yet
(login CSRF). The token key is derived from the signing key with its own
HKDF salt, so tokens survive restarts and reuse no other key material;
gorilla/csrf supplies crypto/rand generation and constant-time compare.

The form routes sit in a chi group behind the middleware; the hidden
token field is rendered into login.html and generator.html. In local
plaintext HTTP mode (debug) requests are marked plaintext so the library
does not demand an https Referer or set a Secure cookie the browser
would withhold; in production, behind the TLS-terminating proxy, it
enforces its https Referer origin check.

model: claude-opus-4-8
clawbot force-pushed issue-93-csrf from 9e745f7b84 to 6a753f14c4 2026-09-21 14:57:25 +02:00 Compare
Some required checks failed
check / check (push) Failing after 0s
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin issue-93-csrf:issue-93-csrf
git checkout issue-93-csrf
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#117