security: add headers middleware, session regeneration, and body size limits (#41)
All checks were successful
check / check (push) Successful in 1m47s
All checks were successful
check / check (push) Successful in 1m47s
## 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 Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de> Reviewed-on: #41 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #41.
This commit is contained in:
34
README.md
34
README.md
@@ -724,7 +724,7 @@ webhooker/
|
||||
│ ├── logger/
|
||||
│ │ └── logger.go # slog setup with TTY detection
|
||||
│ ├── middleware/
|
||||
│ │ └── middleware.go # Logging, CORS, Auth, Metrics, MetricsAuth
|
||||
│ │ └── middleware.go # Logging, CORS, Auth, Metrics, MetricsAuth, SecurityHeaders, MaxBodySize
|
||||
│ ├── server/
|
||||
│ │ ├── server.go # Server struct, fx lifecycle, signal handling
|
||||
│ │ ├── http.go # HTTP server setup with timeouts
|
||||
@@ -775,14 +775,21 @@ Applied to all routes in this order:
|
||||
|
||||
1. **Recoverer** — Panic recovery (chi built-in)
|
||||
2. **RequestID** — Generate unique request IDs (chi built-in)
|
||||
3. **Logging** — Structured request logging (method, URL, status,
|
||||
3. **SecurityHeaders** — Production security headers on every response
|
||||
(HSTS, X-Content-Type-Options, X-Frame-Options, CSP, Referrer-Policy,
|
||||
Permissions-Policy)
|
||||
4. **Logging** — Structured request logging (method, URL, status,
|
||||
latency, remote IP, user agent, request ID)
|
||||
4. **Metrics** — Prometheus HTTP metrics (if `METRICS_USERNAME` is set)
|
||||
5. **CORS** — Cross-origin resource sharing headers
|
||||
6. **Timeout** — 60-second request timeout
|
||||
7. **Sentry** — Error reporting to Sentry (if `SENTRY_DSN` is set;
|
||||
5. **Metrics** — Prometheus HTTP metrics (if `METRICS_USERNAME` is set)
|
||||
6. **CORS** — Cross-origin resource sharing headers
|
||||
7. **Timeout** — 60-second request timeout
|
||||
8. **Sentry** — Error reporting to Sentry (if `SENTRY_DSN` is set;
|
||||
configured with `Repanic: true` so panics still reach Recoverer)
|
||||
|
||||
Additionally, form endpoints (`/pages`, `/sources`, `/source/*`) apply a
|
||||
**MaxBodySize** middleware that limits POST/PUT/PATCH request bodies to
|
||||
1 MB using `http.MaxBytesReader`, preventing oversized form submissions.
|
||||
|
||||
### Authentication
|
||||
|
||||
- **Web UI:** Cookie-based sessions using gorilla/sessions with
|
||||
@@ -797,8 +804,13 @@ Applied to all routes in this order:
|
||||
|
||||
- Passwords hashed with Argon2id (64 MB memory cost)
|
||||
- Session cookies are HttpOnly, SameSite Lax, Secure (prod only)
|
||||
- Session regeneration on login to prevent session fixation attacks
|
||||
- Session key is a 32-byte value auto-generated on first startup and
|
||||
stored in the database
|
||||
- Production security headers on all responses: HSTS, X-Content-Type-Options
|
||||
(`nosniff`), X-Frame-Options (`DENY`), Content-Security-Policy, Referrer-Policy,
|
||||
and Permissions-Policy
|
||||
- Request body size limits (1 MB) on all form POST endpoints
|
||||
- Prometheus metrics behind basic auth
|
||||
- Static assets embedded in binary (no filesystem access needed at
|
||||
runtime)
|
||||
@@ -871,10 +883,18 @@ linted, tested, and compiled.
|
||||
failures per target, opens after 5 failures (30s cooldown),
|
||||
half-open probe to test recovery
|
||||
|
||||
### Completed: Security Hardening
|
||||
- [x] Security headers middleware (HSTS, CSP, X-Frame-Options,
|
||||
X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
|
||||
([#34](https://git.eeqj.de/sneak/webhooker/issues/34))
|
||||
- [x] Session regeneration on login to prevent session fixation
|
||||
([#38](https://git.eeqj.de/sneak/webhooker/issues/38))
|
||||
- [x] Request body size limits on form endpoints
|
||||
([#39](https://git.eeqj.de/sneak/webhooker/issues/39))
|
||||
|
||||
### Remaining: Core Features
|
||||
- [ ] Per-webhook rate limiting in the receiver handler
|
||||
- [ ] Webhook signature verification (GitHub, Stripe formats)
|
||||
- [ ] Security headers (HSTS, CSP, X-Frame-Options)
|
||||
- [ ] CSRF protection for forms
|
||||
- [ ] Session expiration and "remember me"
|
||||
- [ ] Password change/reset flow
|
||||
|
||||
Reference in New Issue
Block a user