AOSSIE-Org / AOSSIE-Org/DebateAI

[BUG]: Password reset code stored in cleartext — vulnerable to offline brute-force by anyone with DB read access

オープン
#507 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る
bug
主要言語
TypeScript
スター
84
フォーク
198
平均マージ
2日 19時間
マージ済み PR(30日)
30

説明

### Bug Description

The 6-digit password reset code is stored in the database in cleartext (`resetPasswordCode` field), and `VerifyForgotPassword` compares the submitted code directly against it. Because the code is only 6 digits (~1,000,000 possibilities), anyone who can read the database (a leaked backup, a compromised read replica, an insider, or a breach) can brute-force all possible codes offline and reset any user's password.

**Affected code** (`backend/controllers/auth.go`):
- `ForgotPassword` stores the raw code:
```go
"resetPasswordCode": resetCode,
```
- `VerifyForgotPassword` matches on the raw code:
```go
FindOne(dbCtx, bson.M{"email": request.Email, "resetPasswordCode": request.Code})
```

**Why a plain hash isn't enough:** a fast generic hash (e.g. SHA-256) of a 6-digit code is trivially reversible — an attacker just hashes all 10^6 codes and matches. So the standard fixes are:
1. **Store a server-keyed HMAC** of the code (HMAC with a secret key from config), and compute the same HMAC during confirmation. Without the server key, a DB reader can't precompute matches. OR
2. **Use a high-entropy random token** (e.g. 32+ bytes) instead of a 6-digit code, so brute-force is infeasible even if stored as-is or plainly hashed.

This was flagged by CodeRabbit (CWE-312, Cleartext Storage of Sensitive Information) during review of #485. It's pre-existing behavior — #485 only added code expiry — so it's tracked here as a separate follow-up.

Note: #485 already reduces the exposure window by expiring codes after 15 minutes, but the offline brute-force risk on a stored code remains until the code is HMAC'd or replaced with a high-entropy token.

@Ri1tik flagged by CodeRabbit during review of my #485 PR (reset-code expiry). It's pre-existing and separate from that PR, so I've filed it here as a follow-up. Could you assign this to me? I'll implement it as a server-keyed HMAC of the reset code (with the same HMAC computed at confirmation) so the stored value isn't brute-forceable, keeping the change scoped to the reset flow.

### Steps to Reproduce

1. Request a password reset for any account.
2. Inspect the user document in MongoDB (e.g. via mongosh or a DB viewer).
3. Observe `resetPasswordCode` is stored as the plain 6-digit code.
4. An attacker with read access could match/brute-force it offline against the confirmation logic.

### Logs and Screenshots

backend/controllers/auth.go — ForgotPassword:
"resetPasswordCode": resetCode, // stored in cleartext

backend/controllers/auth.go — VerifyForgotPassword:
FindOne(..., bson.M{"email": request.Email, "resetPasswordCode": request.Code}) // direct match

Flagged by CodeRabbit as CWE-312 during review of #485.

### Environment Details

- File: backend/controllers/auth.go (ForgotPassword, VerifyForgotPassword)
- Backend: Go / MongoDB
- Branch: main
- Related: follow-up to #485 (which adds expiry); this is a separate hardening concern (cleartext storage)
- Suggested fix: HMAC the code with a server-side secret, or switch to a high-entropy token

### Impact

Medium - Feature works but has issues

### Code of Conduct

- [x] I have joined the [Discord server](https://discord.gg/hjUhu33uAn) and will post updates there
- [x] I have searched existing issues to avoid duplicates

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

Start in backend/controllers/auth.go at ForgotPassword and VerifyForgotPassword, then trace the existing reset-code configuration and expiry flow from related issue #485. Done means the database no longer stores a directly brute-forceable reset code, confirmation still succeeds for valid codes, and expired codes remain rejected.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
go, mongodb
領域
authentication, backend, security
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
62/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。