Skip to content

Add rate limiting and Origin check to /api/subscribe #69

@lwwmanning

Description

@lwwmanning

Surfaced during the supply-chain hardening audit in #68.

Location: src/app/api/subscribe/route.ts

Issue: Public POST endpoint with no rate limit, no Origin/Referer check, and no body-size cap. Every call instantiates a Resend client and hits the paid Resend API. Specifically:

  • Trivially DoS-able from any HTTP client.
  • Burns through the Resend monthly contact-creation quota.
  • Cross-origin POSTs are not rejected (browser CORS doesn't apply to non-browser clients).
  • alreadySubscribed: true in the success response reveals whether an email is already in the list — minor enumeration concern for a public mailing list, but worth closing.

Proposed fix:

  1. Reject requests whose Origin (or Referer, on browsers that strip Origin) doesn't match the deployed site origin. Always return 403 for cross-origin POSTs.
  2. Add an IP-keyed rate limit. Two reasonable options:
    • Simpler: in-memory bucket on Fluid Compute (per-instance, decent enough at our traffic).
    • Stronger: Vercel BotID (which also catches automation) or an Upstash Redis bucket via the Vercel Marketplace.
  3. Cap request body size (1KB is plenty for {email: "..."}).
  4. Return the same success payload regardless of alreadySubscribed — drop the field.

Threat model context: the parent PR (#68) closes the dependency-side supply-chain gaps. This issue covers the application-side attack surface that consumes those dependencies.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsecuritySecurity findings, hardening, and vulnerability disclosure

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions