Skip to content

docs: added ADR for abuse prevention and ratelimiting - #472

Open
conblem wants to merge 2 commits into
mainfrom
adr/abuse
Open

docs: added ADR for abuse prevention and ratelimiting#472
conblem wants to merge 2 commits into
mainfrom
adr/abuse

Conversation

@conblem

@conblem conblem commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • added ADR for abuse prevention and ratelimiting

Validation

Release notes / changeset

  • No changeset required

Related

@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nextgen Ready Ready Preview, Comment Jul 28, 2026 8:59am
nextgen-docs Ready Ready Preview, Comment Jul 28, 2026 8:59am
nextgen-mock-zitadel Ready Ready Preview, Comment Jul 28, 2026 8:59am

Request Review

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ No Changeset found

Latest commit: 0a56943

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
vercel Bot temporarily deployed to Preview – nextgen-mock-zitadel July 9, 2026 08:06 Inactive
@vercel
vercel Bot temporarily deployed to Preview – nextgen-docs July 9, 2026 08:06 Inactive
@muhlemmer
muhlemmer self-requested a review July 10, 2026 05:38
Comment on lines +68 to +71
Proof verification updates the credential/factor counter in the same logical
operation as the verification result. A failed proof increments the counter and
records the failure time. A successful proof resets the counter for that proof
type and records the successful verification time.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refine "operation" a bit further. Do you mean Request or DB Transaction? I'm asking because we need to be careful here. Given:

  1. A secret like a password is obtained from the database before it can be verified.
  2. Verification is a hash operation which is typically bound to CPU time
  3. The counter needs to be updated on failure.

If this all happens in the same transaction, we risk DB pool exhaustion as the a connection stays occupied mid-transaction for the duration of the crypto operation. Hence we should specify a behavior that reading a hashed secret should happen in a first transaction that gets terminated. Post-check updates should happen in a second transaction.

Developing further on this I would propose to update the counter when the secret is read for verification, within the same round-trip if possible. This way we can protect against parallel brute-forcing. Updating the counter post-failure allows a short time window of sending multiple password attempts in parallel requests, which may then result in exceeding the policy.

My proposed request flow would be:

  1. Initial transaction:
    1. Secret is obtained and counter is increased. The query returns the old value of the counter.
    2. Policy check on counter -> lock user / block request if policy is exceeded.
    3. Commit
  2. Perform verification / crypto operation
  3. Second transaction:
    1. Reset counter on success
    2. Push related success / failure events (audit log ADR is not merged yet, but here is where that should go, IMO)
    3. Commit

CC @adlerhurst?

Comment on lines +73 to +83
The credential/factor counter drives progressive controls:

- after each failed proof, calculate an exponential backoff delay from the
current failure count, bounded by the fixed initial maximum delay

The initial backoff defaults are fixed service behavior, not customer-facing
configuration:

- failures 1-4: no artificial delay
- failure 5: 10 second delay
- each later failure doubles the delay, capped at 5 minutes

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should the back-off be enforced?

  1. Do we flat-out deny the request with a 423 or 429? (cheapest, easy to implement)
  2. Current Zitadel delays the response using a tarpit. I'm not a big fan of this because it keeps the HTTP connection open for the duration of tarpit. Platforms like GCP have a max of concurrent requests on a container, so it is very easy to exceed the budget by sending invalid passwords and sending all available connections into the tarpit.
  3. Use tech like ALTCHA.1 They provide both a Go and NPM package which allows setting a PoW target which we can increase on failure, moving the cost of brute-forcing entirely to the client. More failed attempts will result in higher CPU consumption for the client. (little more expensive to implement)

Footnotes

  1. Originally proposed on slack: https://zitadel.slack.com/archives/C098X56G06R/p1777366864101389

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a list of positives / negatives (risks) for the decisions taken in this ADR?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Architecture Decision Record (ADR 041) defining an initial approach for abuse prevention (rate limiting, brute-force mitigation, CAPTCHA escalation) and consistency requirements for auth-critical reads, and registers it in the ADR index.

Changes:

  • Add ADR 041: “Abuse Prevention and Operational Resiliency”.
  • Update docs/adrs/README.md to include ADR 041 in the ADR index table.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
docs/adrs/README.md Adds ADR 041 to the ADR index table.
docs/adrs/041-abuse-prevention-operational-resiliency.md Introduces the new ADR content for abuse prevention and operational resiliency decisions.

Comment thread docs/adrs/README.md
| [038](038-user-credential-migration-and-recovery.md) | User Credential Migration and Recovery | Proposed | Transparent password rehash-on-verify via passwap; passkey migration (soft second-passkey nudge, ROR for domain/`rpId` changes) and single-secret TOTP rotation; last-factor deregistration safeguard; email magic-link / one-time code as both a primary auth method and a recovery path, alongside recovery codes. |
| [039](039-signing-key-rotation-and-incident-response.md) | Signing Key Rotation and Incident Response | Proposed | Per-project signing keys with `active_from`/`retired_at`, a propagation window and a grace period, auto-rotating every 30 days and retired by supersession rather than by an expiry clock; emergency signing-key and KEK/DEK compromise handling, where KEK-and-database compromise means rotating every secret rather than re-wrapping it. |
| [040](040-tenant-login-templates-editable-config.md) | Tenant Login Templates as Editable Config | Proposed | Login templates become an editable-config resource: immutable per-project branding revisions, latest-revision resolution on flow responses, CLI-authoritative LiquidJS validation with a lexical Go gate, sibling-`.liquid` local dialect, and an ejectable design catalog. |
| [041](041-abuse-prevention-operational-resiliency.md) | Abuse Prevention and Operational Resiliency | **Proposed.** Edge-first rate limiting, durable brute-force counters, explicit CAPTCHA/risk gates, and consistency-safe auth reads for PostgreSQL and Spanner. |
Comment on lines +10 to +12
operational resiliency before implementation. The current POC has auth-attempt
state and CAPTCHA design surfaces, but no cohesive policy for rate limits or
bruteforce mitigation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[ARCH-ADR] Define Architecture for Abuse Prevention and Operational Resiliency

3 participants