Skip to content

fix: rate-limit /account/ email change and reject empty emails#1771

Open
oleksandr-nc wants to merge 1 commit into
masterfrom
fix/noid/email-change-rate-limit
Open

fix: rate-limit /account/ email change and reject empty emails#1771
oleksandr-nc wants to merge 1 commit into
masterfrom
fix/noid/email-change-rate-limit

Conversation

@oleksandr-nc
Copy link
Copy Markdown
Collaborator

Summary

  • Rate-limit email-change operations on /account/ and reject empty submissions, to prevent confirmation-mail abuse via rapid repeated changes from a single account.
  • Reuses allauth's existing manage_email action (tightened from its 10/m/user default to 3/h/user) so the budget is shared with allauth's /email/ view — an attacker can't bypass by switching URLs.
  • Supersedes fix: add rate limiting to email update operation in account details page #1729: that approach shared a budget with reset_password and gated all POSTs (including newsletter toggle / name edits).

Known limitations (acknowledged residuals)

  • Multi-account abuse — signup is captcha-floored but not eliminated. A per-target-email key (e.g. `"3/h/user,5/d/key"`) was discussed and excluded from this scope, as it can lock out legitimate users trying to claim an address that an attacker has burned through. Adding it later is a one-line change (`ACCOUNT_RATE_LIMITS` value + `key=new_email` to `consume()`).
  • Per-worker cache — production has no `CACHES` configured per `docs/configuration.rst`, so Django falls back to per-process `LocMemCache`. With 10 uWSGI processes per `docs/prodinstalldocker.rst`, the effective limit is up to `N_workers × 3/h/user`. The same caveat already applies to the existing `reset_password` and `login_failed` limits; a separate ops PR to configure shared cache (Redis/Memcached) is the proper remediation.
  • Concurrent-request race — allauth's `_consume_rate` is non-atomic (`cache.get → mutate → cache.set`). A parallel burst from a single user can exceed the configured budget by ~1 hit (measured: 20 simultaneous threads → 4 successes vs. 3 budget). Pre-existing in allauth; affects the existing `/email/` limit identically.

Related

@oleksandr-nc oleksandr-nc force-pushed the fix/noid/email-change-rate-limit branch 2 times, most recently from da7a3f8 to 0117dce Compare May 18, 2026 16:13
Signed-off-by: Oleksander Piskun <oleksandr2088@icloud.com>
@oleksandr-nc oleksandr-nc force-pushed the fix/noid/email-change-rate-limit branch from 0117dce to bc1f877 Compare May 18, 2026 17:30
@oleksandr-nc oleksandr-nc marked this pull request as ready for review May 18, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant