Skip to content

Rate-limit the public, unauthenticated write endpoints #2252

Description

@maebeale

Four endpoints accept unauthenticated POSTs and create records / send email. None are throttled — the only defence is a hidden honeypot field, which stops naive bots but nothing deliberate.

Endpoints

Endpoint Controller Creates
POST /f/:slug PublicFormsController#create Person, FormSubmission, 2 emails
POST /events/:event_id/public_registrations Events::PublicRegistrationsController#create Person, EventRegistration, FormSubmission, emails
POST /events/:event_id/bulk_payment_form_submissions Events::BulkPaymentFormSubmissionsController#create Person, FormSubmission, emails
POST /contact_us ContactUsController#create email

/f/:slug is the most exposed: it is always open, whereas the event endpoints are gated behind ensure_registerable.

Approach

Rails 8.1 ships ActionController::RateLimiting, so no new gem:

rate_limit to: 5, within: 1.minute, only: :create,
  with: -> { redirect_to public_form_path(@form.slug), alert: "Too many submissions — please wait a minute." }

Keys on request.remote_ip by default and scopes per controller.

Storage

Uses config.cache_store, which is already suitable in both real environments:

  • production — :solid_cache_store (DB-backed, so the counter is shared across processes)
  • development — :memory_store

Decided: no spec for the rate limit

Test env is :null_store, so increment no-ops and a limit never trips — it can't be spec'd without first changing the test cache store (stub Rails.cache, pass an explicit store:, or switch the env to :memory_store).

We are deliberately not doing that. Writing the spec means solving the cache-store question and adds roughly 15 minutes to every suite run for a one-line, framework-provided guard. Not worth it. Ship the rate_limit calls unspec'd and verify by hand in development (where :memory_store makes the limit live).

Revisit only if a future limit carries real logic in its by: or with: lambda.

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions