PST-015: The per-account sign-in lockout reveals which username belongs to which address, and lets a stranger lock anyone out for five minutes #744
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#744
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Found in the security audit of 2026-10-10 (
SECURITY-AUDIT-2026-10-10.md), run againstmainatc30851bfrom 8 to 10 October 2026, as PST-015. Severity: Low.The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it.
src/postulo/accounts/adapter.py(40-61; 71-95)Who can do it, and what they need. A stranger on the internet who knows one identifier of a target (a username — confirmable through the signup form when registration is open, or seen anywhere a username is printed — or an email address) and wants to confirm the other, or confirm that a candidate address belongs to a known account. Precondition: the sign-in page reachable; six POSTs within allauth's 10/min/IP limit, or from more than one address.
How it works.
login=alice(one known username). allauth'slogin_failedlimit (5/5m/key) is now spent under the keyaccount:<alice.pk>because_get_login_attempts_cache_keyresolves whatever was typed to the account id.login=candidate@example.org. If that address belongs to alice (primary email or any allauth EmailAddress, verified or not), the key resolves to the sameaccount:<pk>and the answer is "Too many failed login attempts. Try again later."; any other address — another account's or none — answers "The username and/or password you specified are not correct." Verified by scratch test:alice@example.org→ locked message;bob@example.organdnobody@example.org→ normal failure. The reverse direction works the same (lock under the email, then probe candidate usernames). allauth's own per-key limit (keyed on the typed string) did not have this property; the project's change (#489) introduced the cross-identifier linkage.Impact. An attacker who has a username (public-ish) can confirm which email address it belongs to, or confirm that an email address has an account on this instance when combined with any other identifier; it also provides a five-minute lockout of any targeted account from the outside (DoS, which the threat model excludes). This weakens the stated promise that the sign-in surface "never confirms whether a record exists".
Evidence.
Verification (verdict: confirmed; severity after verification: Low). Keep low. Precision: it links two identifiers of one account, which needs one already known; it does not on its own reveal that an email or username has an account. The DoS aspect is excluded by the threat model. Possible remediation: keep per-account counting but make the visible response to a locked key identical to the normal failure (the lock still stops the guess), or add a per-typed-string count beside the per-account one.
Suggested remediation. Make the refusal indistinguishable: once any key for the typed identifier is spent, answer the generic "not correct" sentence (or always show the lockout sentence only when the per-name key is spent as well), or count under both the account key and the name key and refuse on either while rendering the same text. Alternatively keep the account-keyed count server-side but never reveal it: on lockout, silently reject with the ordinary message.
Also covered by this issue.
src/postulo/accounts/adapter.py(71-95); Informational). Denial of password sign-in for a chosen person; the threat model excludes denial of service, so this is recorded rather than rated. Noted because the same mechanism produces the linkage oracle above.Fixed in
63532a151on main.