Skip to content

sec(auth): MFA recovery-code regeneration and enabled-path disable still do whole-row writes #527

Closed

Description

@cristim

Summary

After #508, MFA recovery-code regeneration and the enabled-path disable still do whole-row writes with stale reads. EnableMFA has no database-side expiry check.

Location (origin/main 496d9d7, internal/auth/service_mfa.go)

  • MFARegenerateRecoveryCodes (:457) needs a current TOTP but no password, and writes the whole row through store.UpdateUser (~:477).
  • MFADisable (:419) enabled path writes the whole row through store.UpdateUser (~:446). It can double-spend a recovery code against a concurrent login and revert concurrent fields.
  • MFAEnable (:366) calls store.EnableMFA (~:386); the pending-secret expiry is checked only on the value read before the write, not in SQL.

Failure scenario

A recovery code is consumed by a login while a disable request holds a stale copy of the user row. The disable write restores the consumed code, so it can be used again.

Context

Sibling of #493; related #227, #442, #474 (closed). From the #508 review (merged).

Acceptance

Regenerate and enabled-path disable use column-scoped writes guarded by the values read, and EnableMFA enforces expiry in the SQL write, each with a concurrency test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions