2.6 KiB
2.6 KiB
A07: Identification and Authentication Failures
Last updated: March 1, 2026
Findings addressed
- No explicit account lockout after repeated failed login attempts (brute-force risk).
- Password policy for registration and password updates was too weak (length-only).
Fixes implemented
- Added login lockout controls:
- Tracks failed login attempts per normalized email in server memory.
- Locks login for a configurable window after threshold failures.
- Returns
429with codeLOGIN_LOCKEDandRetry-Afterheader during lockout.
- Added strong password policy:
- Minimum length
12. - Requires lowercase, uppercase, number, and symbol.
- Applied to:
/auth/registerpassword./me/passwordnew password.
- Added auth hardening configuration:
AUTH_MAX_FAILED_ATTEMPTS(default:5)AUTH_LOCKOUT_WINDOW_MS(default:900000, 15 minutes)
- 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
passwordChangedAtso existing sessions become invalid.
Files changed
api/src/server.tsapi/src/env.ts.env.exampleapi/tests/auth.routes.test.tsapi/tests/identification-auth-failures.test.tsapi/vitest.security.config.tsapi/tests/forgot-password.security.test.tsapi/prisma/schema.prismaapi/prisma/migrations/20260302000000_add_password_changed_at/migration.sql
Verification
Dedicated security suite command (executed):
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:
- Runtime checks weak password rejection for registration and
/me/passwordupdate flow. - Runtime checks lockout threshold/window behavior with configured
AUTH_MAX_FAILED_ATTEMPTSand verifiesLOGIN_LOCKEDresponse +Retry-After.
Runtime auth flow checks added in auth.routes.test.ts:
- Rejects weak passwords on registration.
- Locks login after repeated failed attempts.
Run this in an environment with PostgreSQL running to verify runtime behavior:
cd api
npm test -- tests/auth.routes.test.ts tests/identification-auth-failures.test.ts
Residual notes
- 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.