Skip to content

[Security] IP-based rate limiting on worker login, recovery, and MFA via metrics #1875

Description

@jstanden

Background

Several authentication-adjacent endpoints today throttle by target identity rather than by source IP:

  • Worker login (features/cerberusweb.core/api/uri/login.php _routeAuthenticate) — counts worker.login.failed activity entries, keyed to $worker_id. Credential stuffing across many accounts bypasses this since each victim's counter sees only one attempt.
  • Password recovery (_routeRecover) — keyed on recover:worker:%d cache entry. An attacker spraying recovery requests across many emails isn't slowed.
  • MFA TOTP challengemfa.fail_count is per-login-state. Re-entering the flow gets a fresh counter.

Cerb's standing recommendation is that IP-level rate limiting belongs in nginx/WAF, and that's still the right primary layer. But an in-app, per-IP counter via the metrics service would be a useful defense-in-depth on installs where a WAF isn't configured.

Proposal

Use the metrics service to track failed attempts keyed on source IP across:

  • auth.login.failed (worker password)
  • auth.recover.requested (worker password recovery code send)
  • auth.recover.failed (recovery code wrong-guess)
  • auth.mfa.failed (TOTP wrong-guess)

When a per-IP counter crosses a threshold within a short window (e.g. ≥20 in 15 min), apply progressive slowdown / temporary block.

Dependency

Requires #1554 (Per-metric retention periods) to land first. IP-keyed series have effectively unbounded cardinality (every probing IP gets its own series) and the data is only useful for ~15–60 minutes — keeping it forever would balloon the metrics tables on any internet-exposed Cerb. Need a per-metric retention TTL knob (e.g. expire `auth.login.failed.by_ip` 1d series after 7d) before this is worth shipping.

Context

This came out of an internal login + SSO audit (2026-05-06) as finding F9. F1–F5 from that audit have already shipped to 11.1.9.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions