78 lines
3.1 KiB
Markdown
78 lines
3.1 KiB
Markdown
# Password Security Hardening Plan
|
|
|
|
## Scope
|
|
- Applies to auth and password flows in:
|
|
- `index.js`
|
|
- `auth/authEmail.js`
|
|
- `mongoDB.js`
|
|
|
|
## 1. Replace insecure reset flow with single-use token login
|
|
- Problem:
|
|
- Current flow resets by username and emails a plaintext temporary password.
|
|
- Implementation:
|
|
- Keep `POST /resetPassword` as token request endpoint:
|
|
- Accept identifier (email/username).
|
|
- Always return generic success response.
|
|
- If account exists, create one-time login token with short TTL (15-30 min), store hashed token, email link.
|
|
- Add `POST /password/token-login`:
|
|
- Accept token.
|
|
- Validate token (exists, not expired, unused), mark used atomically, then create normal auth session cookies.
|
|
- Data model:
|
|
- New collection `password_login_tokens` with fields:
|
|
- `userId`, `tokenHash`, `expiresAt`, `usedAt`, `createdAt`, `requestMeta`.
|
|
- Add TTL index on `expiresAt`.
|
|
|
|
## 2. Remove credential handling from GET/query
|
|
- Problem:
|
|
- `/signup` and `/login` accept GET and query params for credentials.
|
|
- Implementation:
|
|
- Change auth routes to `POST` only.
|
|
- Read credentials from JSON body only.
|
|
- Reject query-based credential inputs with `400`.
|
|
- Update Swagger docs and clients accordingly.
|
|
|
|
## 3. Add account-aware brute-force protection
|
|
- Problem:
|
|
- Only global IP limiter exists; auth endpoints are not sufficiently protected.
|
|
- Implementation:
|
|
- Add dedicated limiter middleware for auth endpoints (`/login`, `/password/request-reset`, `/password/confirm-reset`):
|
|
- Combined key: normalized username/email + source IP.
|
|
- Lower thresholds and progressive backoff/lockout window.
|
|
- Add telemetry for blocked attempts.
|
|
- Optionally store counters in Redis if horizontally scaled.
|
|
|
|
## 4. Prevent account enumeration
|
|
- Problem:
|
|
- API exposes different responses for unknown user vs wrong password.
|
|
- Implementation:
|
|
- Login: same response for invalid credentials regardless of user existence.
|
|
- Reset request: always same response regardless of account existence.
|
|
- Keep detailed reason only in internal logs/analytics.
|
|
|
|
## 5. Keep strong password hashing policy (clarify bcrypt behavior)
|
|
- Problem:
|
|
- Existing TODO comments incorrectly state bcrypt needs manual salt handling.
|
|
- Implementation:
|
|
- Keep bcrypt (or migrate to Argon2id in a separate change set).
|
|
- Centralize hash policy:
|
|
- cost factor (benchmark-backed; start at 12 if acceptable latency).
|
|
- minimum password length and strength checks.
|
|
- On login, detect outdated hash params and rehash after successful auth.
|
|
|
|
## Suggested rollout order
|
|
1. Tokenized login flow (new endpoint + DB token store).
|
|
2. POST-only auth route enforcement.
|
|
3. Generic auth/reset responses.
|
|
4. Dedicated auth rate limiting.
|
|
5. Hash policy tuning + opportunistic rehash.
|
|
|
|
## Validation checklist
|
|
- Unit/integration tests:
|
|
- token creation, expiry, one-time use, invalid token paths.
|
|
- login generic error response behavior.
|
|
- auth rate limiter trigger and cooldown.
|
|
- query credential rejection.
|
|
- Manual:
|
|
- verify no plaintext password emails are sent.
|
|
- verify token cannot be reused after first successful consumption.
|