Login error responses enable account enumeration via HTTP status codes #8
Reference in New Issue
Block a user
Delete Branch "%!s()"
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?
Problem
The login flow in
AuthService.login()throws different exception types for "user not found" vs "wrong password":ResourceNotFoundException→ HTTP 404ValidationException→ HTTP 422Even though both messages say "Invalid email or password", the HTTP status code reveals whether the account exists.
Affected files
AuthService.java:45-52Attack scenario
An attacker sends login requests and observes the status code:
This enables building a list of valid email addresses before attempting credential stuffing.
Recommended fix
Both cases should throw the same exception type returning the same HTTP status code (e.g., 401 Unauthorized).
Severity
High — account enumeration enables targeted attacks.
👨💻 Kai — Frontend Engineer
The fix happens entirely in
AuthService.java, but the status code change to 401 will touch our SvelteKit error handling — here's what I want to make sure we get right:Frontend error handling in the login flow:
+page.server.tslogin action probably distinguishes responses by status code to decide what message to show. If we're unifying to 401, we need to make sure the action returns the same generic message regardless — no accidentally branching on 404 vs. 422 in the SvelteKit handler.fail()return.No new UI components needed, but I'd like to audit the login
+page.server.tsaction to confirm it's not currently showing different messages for different status codes. If it is, that's a bug on our side too.Questions:
+page.server.tsbranch on the HTTP status from the backend API, or does it always show a fixed message? If it branches, we need to fix that in this same issue."Invalid email or password"message, or will it be empty? I need to know what the frontend can safely render.🔧 Backend Engineer — Spring Boot / PostgreSQL Specialist
High priority and a clean fix — but a few implementation details worth getting right:
The right exception to throw:
AuthenticationFailedException(or reuse Spring Security'sBadCredentialsException) and add a handler inGlobalExceptionHandlerthat maps it to 401 with a fixed body.ResourceNotFoundException(which semantically means 404) for auth failures — the naming leaks the intent even if we remap the status.Timing attack consideration:
UserDetailsand runpasswordEncoder.matches(rawPassword, dummyHash)to normalize timing.Layer placement:
AuthService, not the controller — the controller should only map exceptions to responses viaGlobalExceptionHandler. Confirm the business rule (unified failure) lives in the service, not in atry/catchin the controller.Signup flow:
AuthServicefor the signup path too.Questions:
AuthService.login()currently throwingResourceNotFoundExceptiondirectly, or is it using aUserDetailsServicethat throws Spring Security exceptions? The fix path differs.🧪 QA Engineer
This is a high-value security fix that also needs careful regression testing — changing the login error behavior touches a critical user-facing path. Here's the test matrix I'd want covered:
Unit tests (AuthService):
shouldReturn401WhenEmailDoesNotExist()— previously returned 404, must now return 401shouldReturn401WhenPasswordIsWrong()— previously returned 422, must now return 401shouldReturnSameBodyForBothFailureCases()— message text is identical in both scenariosshouldNotLeakWhetherEmailExistsInErrorBody()— body never says "user not found" vs "password incorrect"Integration tests (full request cycle):
/api/auth/loginwith non-existent email → 401, generic message/api/auth/loginwith valid email + wrong password → 401, same generic message/api/auth/loginwith valid credentials → 200, session establishedTiming test (exploratory / manual):
Regression — existing login tests:
Edge cases:
/api/auth/loginwith empty email field → should this also be 401 or 400/422? Need to decide and document/api/auth/loginwith SQL-like injection in email field → 401 (not a 500, which would indicate the query is executing)Questions:
🔐 Sable — Security Engineer
This is a classic OWASP A07 (Authentication Failures) vulnerability and it's correctly labeled high. The attack scenario in the issue is accurate. A few things to make sure the fix is complete and not just surface-deep:
The status code fix is necessary but not sufficient:
passwordEncoder.matches()even when the user isn't found. Use a pre-computed dummy hash for this purpose — a static constant inAuthService, not a fresh hash each call.Response body uniformity:
Content-Type, andContent-Lengthare identical for both failure cases. Any difference — including whitespace — can be fingerprinted.Signup enumeration:
POST /api/auth/register(or equivalent) likely returns a different error when the email already exists (e.g., 409 Conflict). This is an explicit account enumeration vector. Should be tracked as a companion issue or included in this one's scope.Related attack: credential stuffing
RateLimiter, an API gateway rule, or nginxlimit_req) on the login endpoint? If not, that's the next thing to address after this fix.Audit log for failed logins:
admin_audit_log(or a separate security event log) with the attempted email and source IP — this feeds into incident detection. Is that currently happening?Questions:
GlobalExceptionHandlermappingResourceNotFoundException→ 404 globally? If so, the fix must ensure the new auth exception type is not a subclass ofResourceNotFoundException.🎨 Atlas — UI/UX Designer
The fix is purely backend, but it directly affects what the login screen communicates to the user. Let me make sure the UX intent is consistent:
Error message copy (German UI):
"E-Mail-Adresse oder Passwort ungültig."— no variation based on which branch failed.Error state design on the login form:
--color-errorfor the text and ideally a short descriptive icon.No new components needed for this fix — but if the login form currently highlights individual fields differently for auth failures vs. validation failures, that needs to be unified.
Questions: