← Security and risk guides

How to spec rate limiting and abuse prevention

How-To Security and risk Intermediate 1161010HOWTO-1161010

HOWTO-1161010Security and riskIntermediate

This guide shows you how to spec rate limiting and abuse prevention with SpecDD in a spec-driven development workflow.

Rate limiting is easy to implement in the wrong place. A UI-only throttle, a route-only guard, or an unreviewed bypass can look correct while leaving another path open. A local spec makes the enforcement point, limit key, response, bypass rules, and checks explicit.

Short answer

Put the rate-limit rule in the spec that owns enforcement. Use Must for limit behavior, Must not for bypasses and unsafe responses, Forbids for blocked direct access or unapproved overrides, Handles for limit states, and Done when for tests, observability, and review evidence.

When to use this guide

Use this guide when code:

Steps

1. Choose the enforcement owner

Name the component that decides whether the action may continue.

Spec: Password Reset Rate Limit

Purpose:
  Limit password reset requests to reduce account enumeration and email abuse.

Owns:
  ./password-reset-rate-limit.ts
  ./password-reset-rate-limit.test.ts

Callers should depend on this owner instead of each route inventing its own logic.

2. Define the protected action and limit key

Use project-approved terms and configuration names. Do not invent numbers if the product or security owner has not chosen them.

Accepts:
  password reset request metadata
  normalized email address
  requester network key

Returns:
  allow
  limited

Must:
  Apply the configured reset-request limit per normalized email address.
  Apply the configured network limit per requester network key.

The limit key matters. A limit keyed only by account id may not protect anonymous flows. A limit keyed only by IP may not fit all products. Spec the approved rule.

3. Specify allowed and limited behavior

Must:
  Allow reset requests that are under the configured limits.
  Return the approved limited response when a configured limit is exceeded.
  Record limit decisions for operational metrics.

Must not:
  Reveal whether the email address belongs to an account through the limited response.
  Send reset email after the rate limit returns limited.

Make sure limited behavior stops protected side effects.

4. Write bypass and override rules

Bypasses are high-risk. If they are allowed, specify them clearly.

Must:
  Allow only the approved internal maintenance caller to bypass the reset-request limit.

Must not:
  Allow client-supplied headers to disable rate limiting.
  Treat hidden UI controls as abuse prevention.

Use Forbids for blocked access paths:

Forbids:
  Direct writes to rate-limit counters outside ../rate-limit-store/*
  Unreviewed allowlist files.

5. Specify observability and audit behavior

Rate limits need enough visibility to debug and operate safely.

Must:
  Emit rate-limit metrics for allow and limited decisions.
  Record an audit event when an administrator changes a rate-limit override.

Must not:
  Include raw reset tokens, passwords, or secret values in rate-limit logs.

Use audit logging for security-sensitive override or administrative behavior. Use metrics for operational visibility.

6. Add scenarios for threshold and bypass cases

Scenario: reset limit exceeded
  Given the normalized email address has reached the configured reset-request limit
  When another password reset request is submitted
  Then the request returns the approved limited response
  And no reset email is sent

Scenario: client attempts bypass
  Given a client sends a header that claims rate limiting is disabled
  When the password reset request is submitted
  Then the normal rate limit still applies

Use named configured limits instead of hard-coded numbers unless the number is part of the approved product contract.

7. Put checks in Done when

Done when:
  Under-limit reset request is allowed.
  Exceeded email limit returns the approved limited response without sending email.
  Client-supplied bypass headers do not disable rate limiting.
  Allow and limited decisions emit operational metrics without secrets.

Tests should cover allow, limited, side-effect suppression, and bypass cases.

Example

Spec: Account Export Rate Limit

Purpose:
  Limit account export requests to protect private data exports and backend capacity.

Owns:
  ./account-export-rate-limit.ts
  ./account-export-rate-limit.test.ts

Accepts:
  authenticated account id
  export request metadata

Returns:
  allow
  limited

Must:
  Apply the configured export limit per authenticated account.
  Return the approved limited response when the export limit is exceeded.
  Emit operational metrics for allow and limited decisions.

Must not:
  Start a new export job when the limit returns limited.
  Allow client-supplied headers or query parameters to bypass the limit.
  Log raw export contents.

Forbids:
  Direct writes to export limit counters outside ../rate-limit-store/*

Done when:
  Under-limit account export request is allowed.
  Exceeded export limit prevents export job creation.
  Client-supplied bypass attempts do not disable the limit.
  Metrics do not include raw export contents.

Common mistakes

How to verify the result

Rate limiting is well specified when:

← Security and risk guides