Skip to content

fix: Login timing side-channel reveals user existence (GHSA-mmpq-5hcv-hf2v)#10398

Merged
mtrezza merged 4 commits intoparse-community:alphafrom
mtrezza:fix/GHSA-mmpq-5hcv-hf2v-v9
Apr 5, 2026
Merged

fix: Login timing side-channel reveals user existence (GHSA-mmpq-5hcv-hf2v)#10398
mtrezza merged 4 commits intoparse-community:alphafrom
mtrezza:fix/GHSA-mmpq-5hcv-hf2v-v9

Conversation

@mtrezza
Copy link
Copy Markdown
Member

@mtrezza mtrezza commented Apr 5, 2026

Issue

Login timing side-channel reveals user existence (GHSA-mmpq-5hcv-hf2v)

Tasks

  • Add tests
  • Add changes
  • Add security check
  • Add benchmark

@parse-github-assistant
Copy link
Copy Markdown

parse-github-assistant bot commented Apr 5, 2026

🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review.

Tip

  • Keep pull requests small. Large PRs will be rejected. Break complex features into smaller, incremental PRs.
  • Use Test Driven Development. Write failing tests before implementing functionality. Ensure tests pass.
  • Group code into logical blocks. Add a short comment before each block to explain its purpose.
  • We offer conceptual guidance. Coding is up to you. PRs must be merge-ready for human review.
  • Our review focuses on concept, not quality. PRs with code issues will be rejected. Use an AI agent.
  • Human review time is precious. Avoid review ping-pong. Inspect and test your AI-generated code.

Note

Please respond to review comments from AI agents just like you would to comments from a human reviewer. Let the reviewer resolve their own comments, unless they have reviewed and accepted your commit, or agreed with your explanation for why the feedback was incorrect.

Caution

Pull requests must be written using an AI agent with human supervision. Pull requests written entirely by a human will likely be rejected, because of lower code quality, higher review effort and the higher risk of introducing bugs. Please note that AI review comments on this pull request alone do not satisfy this requirement.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

📝 Walkthrough

Walkthrough

Adds a dummyHash export and changes authentication to always perform a password comparison — using dummyHash when no user or stored password exists — while keeping lockout, login, ACL, and final error behavior intact.

Changes

Cohort / File(s) Summary
Password utility
src/password.js
Added exported dummyHash (bcrypt hash string). hash/compare logic unchanged.
Authentication router
src/Routers/UsersRouter.js
_authenticateUserFromRequest now performs passwordCrypto.compare(password, passwordCrypto.dummyHash) when no user is found; if user.password is missing/empty it compares against dummyHash (discarding result and forcing invalid password). Downstream lockout/ACL/error flows preserved.
Tests
spec/ParseUser.spec.js
New test spies on password.compare to assert single compare with dummyHash for non-existent user and a single compare with a non-dummy hash for an existing user with wrong password; spy is reset between attempts.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Client
    participant Router as UsersRouter
    participant DB as _User (DB)
    participant PW as passwordCrypto
    rect rgba(200,200,255,0.5)
    Client->>Router: Login request (username, password)
    Router->>DB: Query user by username
    DB-->>Router: No user (empty result)
    Router->>PW: compare(password, dummyHash)
    PW-->>Router: compare result (false)
    Router-->>Client: Error INVALID_USERNAME/PASSWORD
    end
Loading
sequenceDiagram
    autonumber
    participant Client as Client
    participant Router as UsersRouter
    participant DB as _User (DB)
    participant PW as passwordCrypto
    rect rgba(200,255,200,0.5)
    Client->>Router: Login request (username, password)
    Router->>DB: Query user by username
    DB-->>Router: User record (may have password)
    Router->>PW: compare(password, user.password || dummyHash)
    PW-->>Router: compare result (true/false)
    alt valid
        Router-->>Client: Success (session/token)
    else invalid
        Router-->>Client: Error INVALID_USERNAME/PASSWORD
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request lacks any description content despite the repository template requiring sections for Issue, Approach, and Tasks including tests and documentation updates. Add a pull request description following the repository template, including: the security issue being addressed (GHSA-mmpq-5hcv-hf2v), the approach taken to fix it, and confirmation of completed tasks like tests and documentation.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Security Check ✅ Passed PR correctly implements timing attack fix (GHSA-mmpq-5hcv-hf2v) by ensuring all authentication paths execute bcrypt.compare() with consistent computational cost, preventing user enumeration via response timing.
Engage In Review Feedback ✅ Passed No review feedback comments are present on this PR; only an auto-reply confirming the review was triggered.
Title check ✅ Passed The PR title starts with the required 'fix:' prefix and clearly describes the security fix addressing a login timing side-channel vulnerability that reveals user existence.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.50%. Comparing base (f7f3542) to head (abe1879).
⚠️ Report is 2 commits behind head on alpha.

Additional details and impacted files
@@            Coverage Diff             @@
##            alpha   #10398      +/-   ##
==========================================
- Coverage   92.51%   92.50%   -0.02%     
==========================================
  Files         192      192              
  Lines       16721    16725       +4     
  Branches      229      229              
==========================================
+ Hits        15470    15471       +1     
- Misses       1229     1232       +3     
  Partials       22       22              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@spec/ParseUser.spec.js`:
- Around line 85-109: Update the test around Parse.User.logIn/signUp to
explicitly assert that logIn rejects (rather than silently allowing a resolve)
and verify the actual arguments passed to passwordCrypto.compare: for the
non-existent user case assert compare was called with the entered password and
the dummy hash (not undefined), and for the existing-user-wrong-password case
assert compare was called with the entered password and the stored user's hash;
use the existing compareSpy on passwordCrypto.compare to inspect the call
arguments after each login attempt and ensure the promise rejection is asserted
(e.g., expect/rejects or explicit fail on unexpected resolve) so the test fails
if logIn unexpectedly resolves or compare was called with undefined.

In `@src/Routers/UsersRouter.js`:
- Around line 111-117: When a user record lacks user.password the code path
returns immediately and thus leaks timing; update the branch that handles
missing passwords in UsersRouter (the logic surrounding passwordCrypto.compare
and passwordCrypto.dummyHash) to call passwordCrypto.compare(password,
passwordCrypto.dummyHash) instead of returning immediately, await/then the
promise and then throw the same Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Invalid username/password.') so both missing-password users and nonexistent
users go through the identical dummy compare timing path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ca157313-93b8-4672-b2f9-39fb3773d49e

📥 Commits

Reviewing files that changed from the base of the PR and between f7f3542 and 06cfc04.

📒 Files selected for processing (3)
  • spec/ParseUser.spec.js
  • src/Routers/UsersRouter.js
  • src/password.js

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Routers/UsersRouter.js`:
- Around line 130-132: The current fallback assigns passwordCrypto.dummyHash to
hashedPassword and returns passwordCrypto.compare(password, hashedPassword),
which lets the dummy value decide authentication; instead, preserve the dummy
compare for timing but never allow it to authenticate: in the branch where
user.password is not a string (use passwordCrypto.dummyHash), still call/await
passwordCrypto.compare(password, passwordCrypto.dummyHash) to consume equivalent
time, but always return false for that case; when user.password is a string,
continue to return the actual passwordCrypto.compare result. Reference:
user.password, passwordCrypto.dummyHash, passwordCrypto.compare, hashedPassword.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f18fb2a1-9ded-4ffc-ad6e-0bad9ddfbeb9

📥 Commits

Reviewing files that changed from the base of the PR and between 06cfc04 and 72035d7.

📒 Files selected for processing (2)
  • spec/ParseUser.spec.js
  • src/Routers/UsersRouter.js
✅ Files skipped from review due to trivial changes (1)
  • spec/ParseUser.spec.js

@mtrezza
Copy link
Copy Markdown
Member Author

mtrezza commented Apr 5, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/Routers/UsersRouter.js (1)

130-134: Add or point to a regression test for the passwordless fallback.

This branch is the security-sensitive part of the fix, and the provided context only mentions coverage for nonexistent vs wrong-password users. A focused spec that makes passwordCrypto.compare() resolve truthy here and still expects /login or /verifyPassword to fail would help prevent the dummy result from becoming authoritative again.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Routers/UsersRouter.js` around lines 130 - 134, Add a regression test
that exercises the passwordless branch in UsersRouter.js: locate the handler(s)
that call passwordCrypto.compare (the login and verifyPassword routes) and
create a test user with user.password not a string (e.g., null) so the code path
with passwordCrypto.dummyHash is hit; stub/mock passwordCrypto.compare to
resolve truthy and assert that the HTTP response for /login and /verifyPassword
still fails (unauthorized) despite the compare resolving truthy, and also verify
passwordCrypto.compare was called with passwordCrypto.dummyHash to prove the
dummy-compare behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Routers/UsersRouter.js`:
- Around line 130-134: Add a regression test that exercises the passwordless
branch in UsersRouter.js: locate the handler(s) that call passwordCrypto.compare
(the login and verifyPassword routes) and create a test user with user.password
not a string (e.g., null) so the code path with passwordCrypto.dummyHash is hit;
stub/mock passwordCrypto.compare to resolve truthy and assert that the HTTP
response for /login and /verifyPassword still fails (unauthorized) despite the
compare resolving truthy, and also verify passwordCrypto.compare was called with
passwordCrypto.dummyHash to prove the dummy-compare behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a96a7aeb-9830-4a4e-bae1-990bba00b232

📥 Commits

Reviewing files that changed from the base of the PR and between c323b42 and abe1879.

📒 Files selected for processing (1)
  • src/Routers/UsersRouter.js

@mtrezza mtrezza changed the title fix: GHSA-mmpq-5hcv-hf2v v9 fix: Login timing side-channel reveals user existence (GHSA-mmpq-5hcv-hf2v) Apr 5, 2026
@mtrezza mtrezza merged commit 531b9ab into parse-community:alpha Apr 5, 2026
23 of 24 checks passed
parseplatformorg pushed a commit that referenced this pull request Apr 5, 2026
# [9.8.0-alpha.6](9.8.0-alpha.5...9.8.0-alpha.6) (2026-04-05)

### Bug Fixes

* Login timing side-channel reveals user existence ([GHSA-mmpq-5hcv-hf2v](GHSA-mmpq-5hcv-hf2v)) ([#10398](#10398)) ([531b9ab](531b9ab))
@parseplatformorg
Copy link
Copy Markdown
Contributor

🎉 This change has been released in version 9.8.0-alpha.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state:released-alpha Released as alpha version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants