Django admin: the per-account login limit counts each spelling of a username, and the email, separately #489
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#489
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 on 2026-09-29 in a read-only review of postulo at
95348908d, reading the admin login's rate limit and trying it in a test.Checked again on 2026-10-01 against
mainat0a5cad587: still present.The admin login's per-account limit (
5/300s/key) is keyed on the username exactly as posted. Authentication does not care about capitals and accepts the email address too, so "boss", "Boss", "BOSS" and "boss@example.org" all reach the same account while each has a bucket of its own.core/admin_site.py:45(key=(request.POST.get("username") or "")[:150]);config/settings/base.py:185-188(AUTHENTICATION_BACKENDS, which includes allauth's backend) and:207(ACCOUNT_LOGIN_METHODS = {"username", "email"});config/settings/base.py:391(ACCOUNT_RATE_LIMITS).[200, 200, 200, 200, 200, 429, 429, 429]; nine wrong passwords for nine spellings (boss,Boss,BOSS, …,boss@example.org,BOSS@example.org) from nine addresses were all200.BOSS,boss@example.organdBOSS@EXAMPLE.ORGwith the right password each signed in asboss.10/m/ip) still applies to them..strip().casefold(). Better, resolve the posted login to the account it would authenticate (username case-insensitively, or a verified email) and key on that account's primary key, falling back to the casefolded string when nothing matches, so the username and the email share one bucket. If the admin login is replaced by a redirect to allauth's sign-in, as proposed for the second-factor bypass, this limit goes with it.Related to #116 (closed), which added this limit.
Done when
tests/security/test_admin_exposure.pyhas a test in which failed attempts spread over case variants of one username and its email address, each from a different address, reach429by the sixth; it fails today.