security: add headers middleware, session regeneration, and body size limits #41

Merged
sneak merged 1 commits from security/headers-session-bodylimit into main 2026-03-05 12:32:56 +01:00
Collaborator

Summary

This PR implements three security hardening measures:

Security Headers Middleware (closes #34)

Adds a SecurityHeaders() middleware applied globally to all routes. Every response now includes:

  • Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy: camera=(), microphone=(), geolocation=()

Session Fixation Prevention (closes #38)

Adds a Regenerate() method to the session manager that destroys the old session and creates a new one with a fresh ID, copying all session values. Called after successful login to prevent session fixation attacks.

Request Body Size Limits (closes #39)

Adds a MaxBodySize() middleware using http.MaxBytesReader to limit POST/PUT/PATCH request bodies to 1 MB. Applied to all form endpoints (/pages, /sources, /source/*).

Files Changed

  • internal/middleware/middleware.go — Added SecurityHeaders() and MaxBodySize() middleware
  • internal/session/session.go — Added Regenerate() method for session fixation prevention
  • internal/handlers/auth.go — Updated login handler to regenerate session after authentication
  • internal/server/routes.go — Added SecurityHeaders globally, MaxBodySize to form route groups
  • README.md — Documented new middleware in stack, updated Security section, moved items to completed TODO

closes #34, closes #38, closes #39

## Summary This PR implements three security hardening measures: ### Security Headers Middleware (closes https://git.eeqj.de/sneak/webhooker/issues/34) Adds a `SecurityHeaders()` middleware applied globally to all routes. Every response now includes: - `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload` - `X-Content-Type-Options: nosniff` - `X-Frame-Options: DENY` - `Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'` - `Referrer-Policy: strict-origin-when-cross-origin` - `Permissions-Policy: camera=(), microphone=(), geolocation=()` ### Session Fixation Prevention (closes https://git.eeqj.de/sneak/webhooker/issues/38) Adds a `Regenerate()` method to the session manager that destroys the old session and creates a new one with a fresh ID, copying all session values. Called after successful login to prevent session fixation attacks. ### Request Body Size Limits (closes https://git.eeqj.de/sneak/webhooker/issues/39) Adds a `MaxBodySize()` middleware using `http.MaxBytesReader` to limit POST/PUT/PATCH request bodies to 1 MB. Applied to all form endpoints (`/pages`, `/sources`, `/source/*`). ## Files Changed - `internal/middleware/middleware.go` — Added `SecurityHeaders()` and `MaxBodySize()` middleware - `internal/session/session.go` — Added `Regenerate()` method for session fixation prevention - `internal/handlers/auth.go` — Updated login handler to regenerate session after authentication - `internal/server/routes.go` — Added SecurityHeaders globally, MaxBodySize to form route groups - `README.md` — Documented new middleware in stack, updated Security section, moved items to completed TODO closes https://git.eeqj.de/sneak/webhooker/issues/34, closes https://git.eeqj.de/sneak/webhooker/issues/38, closes https://git.eeqj.de/sneak/webhooker/issues/39
clawbot added 1 commit 2026-03-05 11:55:28 +01:00
security: add headers middleware, session regeneration, and body size limits
All checks were successful
check / check (push) Successful in 6s
6c6d6c2f6f
- Add SecurityHeaders middleware applied globally: HSTS, X-Content-Type-Options,
  X-Frame-Options, CSP, Referrer-Policy, and Permissions-Policy headers on every
  response.
- Add session regeneration (Regenerate method) after successful login to prevent
  session fixation attacks. Old session is destroyed and a new ID is issued.
- Add MaxBodySize middleware using http.MaxBytesReader to limit POST/PUT/PATCH
  request bodies to 1 MB on all form endpoints (/pages, /sources, /source/*).
- Update README.md: document SecurityHeaders and MaxBodySize in the middleware
  stack, update Security section, move security headers to completed TODO.

Closes #34, closes #38, closes #39
clawbot added the botneeds-review labels 2026-03-05 11:55:41 +01:00
Author
Collaborator

PR created implementing three security hardening issues:

  • Issue #34: SecurityHeaders middleware (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) applied globally
  • Issue #38: Session regeneration on login (new Regenerate() method destroys old session, creates new ID, copies values)
  • Issue #39: MaxBodySize middleware (1 MB limit via http.MaxBytesReader) on form POST endpoints

All tests pass. docker build . succeeds. README updated with new middleware documentation.

PR created implementing three security hardening issues: - [Issue #34](https://git.eeqj.de/sneak/webhooker/issues/34): SecurityHeaders middleware (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) applied globally - [Issue #38](https://git.eeqj.de/sneak/webhooker/issues/38): Session regeneration on login (new `Regenerate()` method destroys old session, creates new ID, copies values) - [Issue #39](https://git.eeqj.de/sneak/webhooker/issues/39): MaxBodySize middleware (1 MB limit via `http.MaxBytesReader`) on form POST endpoints All tests pass. `docker build .` succeeds. README updated with new middleware documentation. <!-- session: agent:sdlc-manager:subagent:d8dbd4e2-0f72-445f-b92f-2d1fc6a888e5 -->
clawbot removed the needs-review label 2026-03-05 12:05:52 +01:00
Author
Collaborator

Review: PASS

All three security hardening issues are correctly implemented:

#34 — Security Headers Middleware

  • All 6 required headers present: HSTS (with includeSubDomains; preload), X-Content-Type-Options: nosniff, X-Frame-Options: DENY, CSP (default-src 'self'), Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy
  • Applied globally via s.router.Use(s.mw.SecurityHeaders()) — positioned correctly after RequestID and before Logging

#38 — Session Fixation Fix

  • Regenerate() method correctly: (1) copies old session values, (2) destroys old session with MaxAge = -1 + ClearUser + Save, (3) creates new session via store.New, (4) restores values, (5) applies matching session options
  • Session options in Regenerate() are identical to those in New() (Path /, MaxAge 7 days, HttpOnly, Secure in prod, SameSite Lax)
  • Called in HandleLoginSubmit after password verification but before SetUser — correct position
  • Gracefully handles the expected error from store.New when the old cookie is now invalid

#39 — Body Size Limits

  • MaxBodySize middleware uses http.MaxBytesReader for POST/PUT/PATCH methods
  • Applied to all three form route groups: /pages, /sources, /source/{sourceID}
  • 1 MB limit is reasonable and defined as a named constant

Integrity Checks

  • No test files modified
  • No linter config (.golangci.yml) changed
  • No Makefile/Dockerfile/CI changes
  • docker build . passes (includes make check: fmt, lint, test, build)
  • README updated accurately with new middleware stack order and security notes
  • Branch is up to date with main (no rebase needed)
## ✅ Review: PASS All three security hardening issues are correctly implemented: ### [#34](https://git.eeqj.de/sneak/webhooker/issues/34) — Security Headers Middleware - All 6 required headers present: HSTS (with `includeSubDomains; preload`), `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, CSP (`default-src 'self'`), `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy` - Applied globally via `s.router.Use(s.mw.SecurityHeaders())` — positioned correctly after RequestID and before Logging ### [#38](https://git.eeqj.de/sneak/webhooker/issues/38) — Session Fixation Fix - `Regenerate()` method correctly: (1) copies old session values, (2) destroys old session with `MaxAge = -1` + `ClearUser` + `Save`, (3) creates new session via `store.New`, (4) restores values, (5) applies matching session options - Session options in `Regenerate()` are identical to those in `New()` (Path `/`, MaxAge 7 days, HttpOnly, Secure in prod, SameSite Lax) ✅ - Called in `HandleLoginSubmit` after password verification but before `SetUser` — correct position - Gracefully handles the expected error from `store.New` when the old cookie is now invalid ### [#39](https://git.eeqj.de/sneak/webhooker/issues/39) — Body Size Limits - `MaxBodySize` middleware uses `http.MaxBytesReader` for POST/PUT/PATCH methods - Applied to all three form route groups: `/pages`, `/sources`, `/source/{sourceID}` - 1 MB limit is reasonable and defined as a named constant ### Integrity Checks - No test files modified ✅ - No linter config (`.golangci.yml`) changed ✅ - No Makefile/Dockerfile/CI changes ✅ - `docker build .` passes (includes `make check`: fmt, lint, test, build) ✅ - README updated accurately with new middleware stack order and security notes ✅ - Branch is up to date with `main` (no rebase needed) ✅ <!-- session: agent:sdlc-manager:subagent:8a100576-59ec-4733-88bc-721f596caf2e -->
clawbot added merge-ready and removed bot labels 2026-03-05 12:09:17 +01:00
sneak was assigned by clawbot 2026-03-05 12:09:32 +01:00
sneak merged commit 1fbcf96581 into main 2026-03-05 12:32:56 +01:00
sneak deleted branch security/headers-session-bodylimit 2026-03-05 12:32:56 +01:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#41