Skip to content

Throttle failed static-login attempts per IP - #858

Merged
rzueger merged 1 commit into
developfrom
claude/flightbox-security-audit-z16av5
Aug 17, 2026
Merged

Throttle failed static-login attempts per IP#858
rzueger merged 1 commit into
developfrom
claude/flightbox-security-audit-z16av5

Conversation

@rzueger

@rzueger rzueger commented Aug 17, 2026

Copy link
Copy Markdown
Member

What

The static username/password login had no rate limiting, so a shared credential
could be guessed with unlimited online attempts. This adds a per-IP
failed-attempt throttle.

Behaviour

  • After 10 failed attempts from one IP within a 15-minute window, further
    attempts from that IP are rejected until the window elapses.
  • A successful login clears the counter, so normal use never accumulates
    toward a block (a couple of typos are harmless).
  • A blocked attempt returns the same result as a wrong password — no
    "rate limited" oracle.
  • The block auto-recovers after the window; no manual/admin unblock.
  • Per-IP only: a remote attacker being throttled never affects other users.

Implementation

  • Reuses the existing windowed-limiter pattern (as in generateSignInCode),
    stored under a new server-only /staticAuthRateLimits node (.read/
    .write = false; written via the Admin SDK).
  • The existing hourly cleanupExpiredSignInCodes job also prunes elapsed
    entries, so the node can't grow unbounded — no new scheduled function.
  • The throttle sits behind the "no static credentials configured" guard, so only
    projects that use static login (i.e. the one static-credential aerodrome) ever
    touch it. Purely additive: only failed attempts, only past the cap.
  • requestHelper.getIp hardened to tolerate a missing headers/connection.

Scope / honesty

This is a best-effort deterrent: the client IP comes from X-Forwarded-For,
so it stops opportunistic guessing, not a determined attacker who rotates IPs.
The durable defenses remain (a) a strong shared credential — the operator's
choice — and (b) migrating this login to the standard mechanisms (e-mail OTP +
passkeys + guest/kiosk). No global or per-username lock is used, deliberately —
that would let an attacker lock out legitimate users of the shared credential.

Tests

functions/auth/modes/static/index.spec.js — blocks after 10 failures (holds
even for a correct password), does not block a different IP, clears on success,
auto-recovers after the window, skips cleanly when no IP; existing behaviour
retained. Plus a prune test in cleanupExpiredSignInCodes.spec.js. Full
functions suite green (436).


Generated by Claude Code

The static username/password login had no rate limiting, so a shared
credential could be guessed with unlimited online attempts. Add a
per-IP failed-attempt counter: after 10 failures from one IP within
15 minutes, further attempts from that IP are rejected until the
window elapses; a successful login clears the counter, so normal use
never accumulates. A blocked attempt returns the same result as a
wrong password, revealing nothing.

The counter reuses the existing windowed-limiter pattern and is
stored under the server-only /staticAuthRateLimits node; the hourly
cleanup job prunes elapsed windows so it cannot grow unbounded. Only
projects that configure static credentials ever touch it.

This is a best-effort deterrent (the client IP comes from
X-Forwarded-For); the durable defenses remain a strong shared
credential and moving this login to the standard mechanisms.

Also make requestHelper.getIp tolerate a missing headers/connection.
@rzueger
rzueger force-pushed the claude/flightbox-security-audit-z16av5 branch from 49463af to 8f9da22 Compare August 17, 2026 11:41
@rzueger
rzueger merged commit 156629f into develop Aug 17, 2026
2 checks passed
@rzueger
rzueger deleted the claude/flightbox-security-audit-z16av5 branch August 17, 2026 11:46
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.

2 participants