Skip to content

feat(opds): exponential backoff on failed authentication - #163

Merged
phildenhoff merged 1 commit into
mainfrom
opds-auth-backoff
Sep 22, 2026
Merged

phildenhoff merged 1 commit into
mainfrom
opds-auth-backoff

Conversation

@phildenhoff

Copy link
Copy Markdown
Member

Problem

The generated password is ~30.5 bits — deliberately short because a human types it on an e-reader. That's fine only if the online attacker can't hammer the server. Today the only friction is the Argon2 verify cost (~3 concurrent, ~100ms each), which still permits sustained guessing at ~60 attempts/second: full exhaustion of 30.5 bits in ~6 months of nonstop grinding.

What this does

Global exponential backoff on consecutive rejected authentication attempts, living on the shared auth handle (both wildcard listeners count against one budget):

  • 3 consecutive rejections open a backoff window: 2s, doubling per additional failure, capped at 60s.
  • While the window is open, attempts are answered 429 Too Many Requests + Retry-After — no hash, no verify.
  • A successful authentication resets the budget (the legitimate user types it right and is immediately fine).
  • Cached rejections count too — replaying the same wrong header can't dodge the counter.

With sustained guessing throttled to ~1–2 attempts/second, the 30.5-bit margin becomes years. This was the condition under which the short generated password was accepted in #161.

Testing

59 tests pass, including two new: the throttle window opens after the threshold and expires back into verification, and a successful authentication resets the budget mid-streak. (The first version of this missed cached rejections — replayed wrong headers bypassed the counter; the tests caught it.)

Repeated failed auth attempts now trigger a global backoff: after three
consecutive rejections the server answers 429 with Retry-After instead of
hashing, doubling from 2s to a 60s cap. A successful authentication resets
it. The state lives on the shared auth handle, so both wildcard listeners
count against the same budget.

This was the condition under which the ~30.5-bit generated password was
accepted: with sustained guessing throttled to ~1-2 attempts per second,
the margin is years.
@github-actions

Copy link
Copy Markdown

libcalibre Test Coverage Report

Overall coverage: 80.16%

📊 Download HTML Report

Coverage breakdown available in the artifacts.

@phildenhoff
phildenhoff enabled auto-merge (squash) September 22, 2026 06:56
@phildenhoff
phildenhoff merged commit 7aad66f into main Sep 22, 2026
7 checks passed
@phildenhoff
phildenhoff deleted the opds-auth-backoff branch September 22, 2026 07:04
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