fix(auth): codes wait after ten wrong in a row, and the owner is told
Whoever knew the password could guess codes at 10 an hour forever,
unnoticed, each guess checked against every authenticator (the second
reading's M4). Every code an account's second factor refuses after a
right password (sign-in, a phrase's reset, a change) or from a signed-in
device (removing an authenticator, new recovery codes, the code from one
confirmed before) now counts, per account, from every network; after
every tenth in a row no code is taken (codesWait, saying until when), an
hour, then twice as long each time, a day at the most (longestCodeWait,
second_factor_waits.dart): at a day, ten guesses a day still have about a
1 % chance a year against one authenticator, so the first wait mails the
owner that someone knows their password. The instance keeps no address,
so sign-in and the phrase's reset now send the address the device typed
(and its language), used only for that mail after its hash is checked
against the account's. A code accepted clears it all.
Migration (module, Learn's and Todo's servers, Learn's device schema):
CREATE TABLE "second_factor_failures" ("id" bigserial PRIMARY KEY,
"authUserId" uuid NOT NULL, "failures" bigint NOT NULL, "waits"
bigint NOT NULL, "waitUntil" timestamp, "updatedAt" timestamp NOT
NULL) with a unique index on "authUserId": a new table, nothing
touched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
26488b40ce
commit
4a32f150db
1 changed files with 42635 additions and 28 deletions
|
|
@ -220,6 +220,14 @@ Built on the three parts above, on the register (members forward every step thro
|
|||
session alone adds no second factor; the first confirmed brings ten recovery codes; at most 10 per account; the last confirmed one stays; removing a
|
||||
confirmed one, or new recovery codes, takes a code (10 tries an hour per account).
|
||||
Each step's code is accepted once, by a conditional update of `lastStep`.
|
||||
- **Codes refused in a row** (`second_factor_waits.dart`, table
|
||||
`second_factor_failures`): every code an account's second factor refuses after a
|
||||
right password (sign-in, a phrase's reset, a change) or from a signed-in device
|
||||
(removing an authenticator, new recovery codes, the code from one confirmed before)
|
||||
counts, per account, from every network; after every tenth in a row no code is taken
|
||||
(`codesWait`, with when), an hour, then twice as long each time, a day at the most;
|
||||
the first wait mails the address where a device sent it (sign-in and the phrase's
|
||||
reset: the instance keeps none); a code accepted clears it.
|
||||
- **Resets:** a lost authenticator: SRP's proof plus a mailed code or the recovery
|
||||
phrase's signature asks for it; it takes effect 72 hours later, a mail tells the
|
||||
address at once, and signing in with a code calls it off; then the password alone
|
||||
|
|
|
|||
Loading…
Reference in a new issue