AOSSIE-Org / AOSSIE-Org/DebateAI
[BUG]: Password reset code stored in cleartext — vulnerable to offline brute-force by anyone with DB read access
- Ngôn ngữ chính
- TypeScript
- Star
- 84
- Fork
- 198
- Merge trung bình
- 2 ngày 19 giờ
- Pull request đã merge (30 ngày)
- 30
Mô tả
### 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
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- go, mongodb
- Lĩnh vực
- authentication, backend, security
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Sôi nổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 62/100