feat: add CSRF protection, SSRF prevention, and login rate limiting #42
Reference in New Issue
Block a user
Delete Branch "security/csrf-ssrf-ratelimit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Security Hardening
This PR implements three security hardening issues:
CSRF Protection (closes #35)
/pages,/sources,/source, and/userroutescsrf_tokenfield added to all 12+ POST forms in templates/webhook(inbound webhook POSTs) and/api(stateless API)SSRF Prevention (closes #36)
ValidateTargetURL()blocks private/reserved IP ranges at target creation time127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1,fc00::/7,fe80::/10, plus multicast, reserved, test-net, and CGN rangesDialContextin the delivery engine for defense-in-depth (prevents DNS rebinding attacks)httpandhttpsschemes allowedLogin Rate Limiting (closes #37)
golang.org/x/time/ratePOST /pages/loginX-Forwarded-ForandX-Real-IPheader support for reverse proxiesFiles Changed
New files:
internal/middleware/csrf.go+ tests — CSRF middlewareinternal/middleware/ratelimit.go+ tests — Login rate limiterinternal/delivery/ssrf.go+ tests — SSRF validation + safe transportModified files:
internal/server/routes.go— Wire CSRF and rate limit middlewareinternal/handlers/handlers.go— Inject CSRF token into template datainternal/handlers/source_management.go— SSRF validation on target creationinternal/delivery/engine.go— SSRF-safe HTTP transport for productioncsrf_tokenfieldsREADME.md— Updated Security section and TODO checklistdocker build .passes (lint + tests + build).PR ready for review. Implements three security hardening issues:
/pages,/sources,/source,/userroutes. Excluded from/webhookand/api.POST /pages/loginusinggolang.org/x/time/rate.All existing tests pass. New tests added for all three features.
docker build .passes (lint + tests + build).✅ Review PASS — PR #42 (CSRF + SSRF + Rate Limiting)
All three security issues verified against requirements.
docker build .passes.#35 — CSRF Protection ✅
crypto/rand— correctsecureCompareusing XOR accumulation — correct (could usecrypto/subtle.ConstantTimeComparebut functionally equivalent)<input type="hidden" name="csrf_token">:login.html(1 form)navbar.html(2 forms — desktop + mobile logout)sources_new.html(1 form)source_edit.html(1 form)source_detail.html(7 forms — delete, add entrypoint, toggle/delete entrypoint, add target, toggle/delete target)/pages,/sources,/source/{sourceID},/user/{username}routes/webhook/{uuid}(inbound webhook POSTs) and/api/v1(stateless API)#36 — SSRF Prevention ✅
ValidateTargetURLchecks at target creation time;NewSSRFSafeTransportwith customDialContextvalidates resolved IPs at connection time (DNS rebinding defense)#37 — Login Rate Limiting ✅
golang.org/x/time/rate: 5 attempts per minute (rate = 5/60 ≈ 0.0833/sec, burst = 5) — arithmetic correctIntegrity Checks ✅
.golangci.ymlunchangeddocker build .(which runsmake check) passesmain(no rebase needed)golang.org/x/time v0.14.0properly added to go.mod/go.sum@clawbot pls fix conflicts
19e7557e88to7f4c40cacaRebased onto
mainto resolve merge conflicts from PR #41 merge.Conflict resolution:
internal/server/routes.go: Merged both middleware stacks —/pagesroute group now applies CSRF, MaxBodySize, and LoginRateLimit./sourcesand/source/{sourceID}groups retain CSRF, RequireAuth, and MaxBodySize.README.md: Combined security documentation from both PRs (security headers + body size limits from PR #41, CSRF/SSRF/rate-limiting from this PR). Updated package layout, security section, and TODO checklist.All tests pass. Docker build succeeds.
✅ Post-Rebase Review PASS — PR #42
Verified all three security features are intact after rebase onto main (which merged PR #41: security headers, session fixation, body limits).
docker build .passes.Middleware Stack Verification ✅
Both PR #41 and PR #42 middleware correctly present in
routes.go:/pages/pageslogin group/user/{username}/sources/source/{sourceID}/webhook/{uuid}No code lost or duplicated in conflict resolution.
#35 — CSRF Protection ✅
csrf_tokenhidden input/pages,/user,/sources,/sourceroutes/webhook(inbound) and/api(stateless)#36 — SSRF Prevention ✅
127/8,10/8,172.16/12,192.168/16,169.254/16,::1/128,fc00::/7,fe80::/100.0.0.0/8,100.64/10(CGN), multicast, reservedNewSSRFSafeTransport()re-resolves hostnames at dial time and validates resolved IPsHandleTargetCreate) + transport-level blocking at delivery time (Engine.client.Transport)#37 — Login Rate Limiting ✅
golang.org/x/time/ratetoken bucketAdditional Checks
.golangci.yml: unchanged ✅go.modadds onlygolang.org/x/time(required for rate limiter) ✅Minor Style Note (non-blocking)
secureCompare()incsrf.gois a hand-rolled constant-time comparison. Consider usingcrypto/subtle.ConstantTimeComparefrom stdlib for the same guarantee with less code. Not a functional issue — the current implementation is correct.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.