-
Notifications
You must be signed in to change notification settings - Fork 3
security(auth): a TOTP code can be replayed within its validity window #849
Copy link
Copy link
Closed
Labels
area:backendGo, /internal, /pkgGo, /internal, /pkgarea:securityCybersecurity and threat intelligenceCybersecurity and threat intelligenceauthenticationAuthenticationAuthenticationpriority:P2NormalNormalpriority:P2-mediumNormal milestone workNormal milestone worksecurityProduct security postureProduct security posturestatus:in-reviewPR openPR opentier:0-trustTrust: security, isolation, evidence integrityTrust: security, isolation, evidence integritytype:securitySecurity defect or hardeningSecurity defect or hardening
Milestone
Description
Activity
Metadata
Metadata
Assignees
Labels
area:backendGo, /internal, /pkgGo, /internal, /pkgarea:securityCybersecurity and threat intelligenceCybersecurity and threat intelligenceauthenticationAuthenticationAuthenticationpriority:P2NormalNormalpriority:P2-mediumNormal milestone workNormal milestone worksecurityProduct security postureProduct security posturestatus:in-reviewPR openPR opentier:0-trustTrust: security, isolation, evidence integrityTrust: security, isolation, evidence integritytype:securitySecurity defect or hardeningSecurity defect or hardening
Problem
A TOTP code is accepted as many times as it is typed within its window. The server checks it with
otp.VerifyTOTP(pquerna/otptotp.Validate, ±1 step, so about 90 s) and never records that it was used:MFASecret.LastUsedAtis written after a login challenge but never read.So someone who watches a user type a code (shoulder-surfing, screen share, a recorded session) and also has the password or an open session can use the same code again within that window. The second factor is then not a second proof, and a replay goes through anywhere a code is checked:
backend/internal/application/auth/mfa_usecase.go—ChallengeMFAUseCase.Executemfa_usecase.go—VerifyMFAUseCase.Executemfa_usecase.go—DisableMFAUseCase.Executebackend/internal/infrastructure/authmfa/gate.go—Gate.VerifyRequiredFound while working on #754 (PR #848).
Acceptance criteria
mfa_secrets, carried by AutoMigrate and by a numbered SQL migration with a down file. Nothing dropped or renamed.TestChallengeMFA_ReplayedCodeIsRefused,TestDisableMFA_ReplayedCodeIsRefused,TestGate_ReplayedCodeIsRefused, one test for an earlier step, and the concurrency test from criterion 3. The existingSuccess/NotFound/Unauthorizedtests stay green.Definition of Done
go test ./...passes, and the Postgres-gated tests pass withDATABASE_URLset on PostgreSQL 16.