feat: brute-force protection for the login endpoint#104
Open
christianromeni wants to merge 1 commit into
Open
Conversation
POST /api/v1/auth/login had no rate limiting or lockout, so a wordlist attack against a known account was practical. LoginThrottle adds two in-memory mechanisms: a per-IP limit (10 attempts/minute) and a per-account lockout (15 minutes after 5 consecutive failures). A locked account also burns a bcrypt comparison before returning 429 so the response timing does not reveal the lockout state. Eviction runs on the existing 5-minute ticker and also drops idle account entries (no lockout, last failure older than 30 minutes) so spraying random emails cannot grow the maps unbounded. In-memory state is per-process and does not survive restarts, matching the documented single-instance constraint; distributed throttling will follow with Redis support.
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
POST /api/v1/auth/loginhad no rate limiting and no account lockout. bcrypt slows a single attempt, but a wordlist attack against a known account was practical.Fix
New
LoginThrottle(in-memory,internal/auth/login_throttle.go) with two mechanisms:Integration in the login handler:
Allow()is checked before the bcrypt comparison.too many login attempts, try again later) - it does not disclose whether the IP limit or the account lockout fired, nor whether the account exists.Memory bounds
Eviction is hooked into the existing 5-minute ticker (no new goroutine). It drops expired IP windows and idle account entries (no active lockout, last failure older than 30 minutes), so spraying random emails cannot grow the maps unbounded.
Known tradeoffs (accepted for this iteration)
c.IP()uses the TCP peer IP (noX-Forwarded-Fortrust configured), so behind a proxy withouttrusted_proxiesset, all clients share one IP bucket. This is a pre-existing, deployment-wide concern (also affects audit logs) and should be documented in the deployment guide.Tests
go test -race ./internal/auth/ ./internal/api/...clean.