fix: prevent user enumeration on password recovery endpoint - #5
anurag6569201 wants to merge 1 commit into
Conversation
Source PR: appwrite#13438 Source head: 3197e0b
⛔ Shipwright · BlockedRecommendation: do not merge PR #5 · Tier
Findings (8)
Fireworks usage: 9,413 input · 846 output · 10,259 total tokens · $0.0026 · 12s · 0 fix iteration(s) Open the Shipwright check for full evidence and the audit bundle. Use |
|
|
||
| $this->assertEquals(201, $response['headers']['status-code']); | ||
| $this->assertNotEmpty($response['body']['$id']); | ||
| $this->assertNotEmpty($response['body']['userId']); |
There was a problem hiding this comment.
Shipwright · HIGH
The test asserts that the response body contains a non-empty userId for a blocked user, but the code sets userId to a random ID for non-deliverable cases.
Impact: The test asserts that the response body contains a non-empty userId for a blocked user, but the code sets userId to a random ID for non-deliverable cases. This test would pass even if the userId is random, but it does not verify that the userId corresponds to the blocked user, which may mask the security issue.
Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
|
|
||
| $this->assertEquals(201, $response['headers']['status-code']); | ||
| $this->assertNotEmpty($response['body']['$id']); | ||
| $this->assertNotEmpty($response['body']['userId']); |
There was a problem hiding this comment.
Shipwright · LOW
The test asserts that the response body contains a non-empty userId for a blocked user, but the code sets userId to a random ID for non-deliverable cases.
Impact: The test asserts that the response body contains a non-empty userId for a blocked user, but the code sets userId to a random ID for non-deliverable cases. This test would pass even if the userId is random, but it does not verify that the userId corresponds to the blocked user, which may mask the security issue.
Suggested fix: Fix the review finding before release.
What does this PR do?
This PR fixes a user enumeration vulnerability in the
POST /v1/account/recoveryendpoint.Previously, the endpoint threw a
404(Exception::USER_NOT_FOUND) for unregistered emails and a401(Exception::USER_BLOCKED) for blocked users. This allowed malicious actors to enumerate registered and active users via an unauthenticated endpoint.This fix standardizes the behavior so that the endpoint always returns a
201 Createdwith an identical response payload structure regardless of whether the account exists, is blocked, or is active.Key Changes:
USER_NOT_FOUNDandUSER_BLOCKED.ID::unique()) for$id,userId, anduserInternalIdto ensure the response payload perfectly mirrors a successful request.Test Plan
Added comprehensive E2E tests in
tests/e2e/Services/Account/AccountCustomClientTest.phpto explicitly verify that the enumeration vulnerabilities are closed:not-found@localhost.test) returns a201 Createdand a valid mock payload structure with nosecretorphrase.201 Createdwith a valid mock payload.To verify locally:
Related PRs and Issues
account.createRecovery/ POST to/account/recoveryallows user enumeration appwrite/appwrite#13435Checklist
Source merge-base:
50861bafc095b5409801bd34f165a714b5703154Source head:
3197e0ba5d2a018b55c8ff3999db585355457dc1