Skip to content

Handle CSRF token error gracefully #4809

@midigofrank

Description

@midigofrank

Describe the bug

Submitting the login form (POST /users/log_in) with a stale CSRF token raises Plug.CSRFProtection.InvalidCSRFTokenError and the user sees the default 403 error page.

The most common trigger for real users: log in, click the browser back button, the cached login form is re-submitted with the pre-login CSRF token. Lightning's UserAuth.new_session/2 calls configure_session(renew: true) |> clear_session(), so the old token no longer matches the (new, empty) session.

You can recreate this by opening the login page on two different tabs and then login on each page without refreshing. Currently, you get this Forbidden page

Image

Sentry

LIGHTNING-NS — 3,397 events, 94 users, ongoing since 2024-11-11.
https://openfn.sentry.io/issues/6056897474/

Bot scanners account for a large share (top 2 IPs = ~54% of recent introspectable events, heavily concentrated Chrome 131 fingerprint, scans of bare GCP IPs), but real users hit it too.

Version number

v2.16.5

Expected behavior

When CSRF validation fails on a browser request, redirect to /users/log_in with a flash like "Your session expired. Please try again." instead of rendering the 403 page.

Proposed fix

  1. Add LightningWeb.Plugs.CSRFProtectionWithFallback that wraps Plug.CSRFProtection.call/2 in try/rescue and on InvalidCSRFTokenError does put_flash |> redirect(to: ~p"/users/log_in") |> halt(). Use it in place of plug :protect_from_forgery in the :browser pipeline.
  2. Add Plug.CSRFProtection.InvalidCSRFTokenError to Lightning.SentryEventFilter @ignored_exceptions (lib/lightning/sentry_event_filter.ex) so any remaining occurrences (non-browser pipelines) stop creating Sentry noise.

The rescue is not a security regression — the token still fails validation, the request is still rejected, the response just becomes a 302 to a GET instead of a 403 page.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions