fix(security): authenticate connect callback and replace Math.random OAuth state#162
Open
anshul23102 wants to merge 3 commits into
Open
fix(security): authenticate connect callback and replace Math.random OAuth state#162anshul23102 wants to merge 3 commits into
anshul23102 wants to merge 3 commits into
Conversation
The state parameter was generated before redirecting to GitHub/Google but was never checked in the callbacks. This made CSRF protection non-functional since any forged callback request would proceed. Fix: store the generated state in a short-lived httpOnly cookie before redirecting, then compare it against the state returned by the provider in the callback. If they do not match the request is rejected with 400. The cookie is cleared immediately after the check regardless of outcome.
…state
Two related security issues in the GitHub OAuth connect flow:
1. The /api/connect/github/callback route had no preHandler. An attacker who
obtained any valid GitHub OAuth code could craft an arbitrary base64 state
payload containing a victim's userId and POST the callback to store their
GitHub token under the victim's account, enabling them to trigger follows
on behalf of the victim.
Fix: add preHandler: [app.authenticate] and verify that the userId
embedded in the state matches the authenticated session before the token
upsert is performed.
2. Both auth.ts and connect.ts used Math.random().toString(36) for OAuth state
tokens. Math.random is not a CSPRNG (only ~42 bits of entropy in V8) and
cannot be safely used for security-sensitive nonces.
Fix: replace with crypto.randomBytes(32).toString('hex') (256 bits of
cryptographically secure entropy) in both files.
Use named import { randomBytes } from crypto to align with upstream
style. Logic is unchanged -- crypto.randomBytes(32) was already the
correct fix on both branches.
Signed-off-by: anshul23102 <anshul23102@iiitd.ac.in>
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.
Fixes #161
Changes
1. Add
preHandler: [app.authenticate]to the connect callback (connect.ts)The callback route now requires a valid session. After decoding the state, the embedded
userIdis compared against the authenticated user's id:This closes the account-takeover path where an attacker with any valid OAuth code could forge a state payload pointing to a victim's userId.
2. Replace
Math.random()withcrypto.randomBytes(auth.ts+connect.ts)Applied to both
generateState()functions. No logic change -- only the entropy source changes.Why the preHandler approach works
The JWT is stored in an httpOnly
tokencookie (set at login). GitHub redirects the user's browser back to the callback URL, which carries all cookies, soapp.authenticatecan verify the session from the cookie in the same way as every other protected route.Testing
GET /api/connect/github-- redirects to GitHub.statecontaining User B's userId while authenticated as User A -- server returnsinvalid_state. (blocked)