fix(auth): count a failed recovery code against MFA lockout #478
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/PMS-694-recovery-code-mfa-lockout"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The recovery-code branch of the login second-factor block returned
Unauthorizedon a miss without callingregister_failed_mfa, somfa_failed_attemptsnever moved andmfa_locked_untilnever armed. An attacker holding the password got unlimited second-factor guesses simply by sendingrecovery_codeinstead ofmfa_code, and because nothing else arms the counter, that also left the lockout inert for the TOTP path.The
!removedbranch now feeds the same per-account counter the TOTP branch does. Symmetrically, an accepted recovery code clears the counter and the lockout via a newclear_mfa_lockouthelper, so a user who locked themselves out of TOTP and then used a recovery code is not still serving the remaining backoff.record_mfa_successis deliberately not reused there: its compare-and-set advancesmfa_last_used_step, and a recovery code is not a TOTP step, so the anti-replay watermark must stay put.Sweep of every second-factor gate: the two login branches are the only ones.
enable_mfaverifies an enrollment code on an already-authenticated session beforemfa_enabledis set,disable_mfare-auths with a password only, and the portal path has no MFA at all.Tests:
failed_recovery_codes_lock_accountpins three wrong recovery codes tomfa_failed_attempts = 3with an armed window and the 4th attempt refused asRateLimited;recovery_code_success_clears_mfa_counterspins a valid code succeeding with two failures banked, leaving the counter at 0, the lockout NULL andmfa_last_used_stepuntouched. Both fail without the change.The PMS-625 audit doc's F5 row is updated to record the finding as tracked here rather than as an open follow-up.
#PMS-694