80 lines
2.6 KiB
Markdown
80 lines
2.6 KiB
Markdown
# A07: Identification and Authentication Failures
|
|
|
|
Last updated: March 1, 2026
|
|
|
|
## Findings addressed
|
|
|
|
1. No explicit account lockout after repeated failed login attempts (brute-force risk).
|
|
2. Password policy for registration and password updates was too weak (length-only).
|
|
|
|
## Fixes implemented
|
|
|
|
1. Added login lockout controls:
|
|
- Tracks failed login attempts per normalized email in server memory.
|
|
- Locks login for a configurable window after threshold failures.
|
|
- Returns `429` with code `LOGIN_LOCKED` and `Retry-After` header during lockout.
|
|
|
|
2. Added strong password policy:
|
|
- Minimum length `12`.
|
|
- Requires lowercase, uppercase, number, and symbol.
|
|
- Applied to:
|
|
- `/auth/register` password.
|
|
- `/me/password` new password.
|
|
|
|
3. Added auth hardening configuration:
|
|
- `AUTH_MAX_FAILED_ATTEMPTS` (default: `5`)
|
|
- `AUTH_LOCKOUT_WINDOW_MS` (default: `900000`, 15 minutes)
|
|
|
|
4. Added forgot-password hardening:
|
|
- Public reset request endpoint always returns a generic success response.
|
|
- Reset token issuance is restricted to verified users.
|
|
- Reset confirmation enforces strong password policy and one-time expiring token usage.
|
|
- Successful reset updates `passwordChangedAt` so existing sessions become invalid.
|
|
|
|
## Files changed
|
|
|
|
1. `api/src/server.ts`
|
|
2. `api/src/env.ts`
|
|
3. `.env.example`
|
|
4. `api/tests/auth.routes.test.ts`
|
|
5. `api/tests/identification-auth-failures.test.ts`
|
|
6. `api/vitest.security.config.ts`
|
|
7. `api/tests/forgot-password.security.test.ts`
|
|
8. `api/prisma/schema.prisma`
|
|
9. `api/prisma/migrations/20260302000000_add_password_changed_at/migration.sql`
|
|
|
|
## Verification
|
|
|
|
Dedicated security suite command (executed):
|
|
|
|
```bash
|
|
cd api
|
|
npx vitest --run -c vitest.security.config.ts
|
|
```
|
|
|
|
Verified output:
|
|
|
|
- Test Files: `5 passed (5)`
|
|
- Tests: `12 passed (12)`
|
|
|
|
Dedicated A07 checks in `identification-auth-failures.test.ts`:
|
|
|
|
1. Runtime checks weak password rejection for registration and `/me/password` update flow.
|
|
2. Runtime checks lockout threshold/window behavior with configured `AUTH_MAX_FAILED_ATTEMPTS` and verifies `LOGIN_LOCKED` response + `Retry-After`.
|
|
|
|
Runtime auth flow checks added in `auth.routes.test.ts`:
|
|
|
|
1. Rejects weak passwords on registration.
|
|
2. Locks login after repeated failed attempts.
|
|
|
|
Run this in an environment with PostgreSQL running to verify runtime behavior:
|
|
|
|
```bash
|
|
cd api
|
|
npm test -- tests/auth.routes.test.ts tests/identification-auth-failures.test.ts
|
|
```
|
|
|
|
## Residual notes
|
|
|
|
1. Current lockout state is in-memory per API instance; for horizontally scaled production, move lockout tracking to a shared store (Redis/DB) for consistent enforcement across instances.
|