Skip to content

fix: app.js reads CSRF cookies in the reverse of the backend's precedence #489

Description

@FlyM1ss

dashboard/frontend/app.js:3910 iterates the CSRF cookie names dev-first:

for (const name of ['atl_csrf', '__Host-atl_csrf']) {

dashboard/backend/csrf.py:79 reads them host-first, unconditionally:

for name in (_HOST_CSRF, _DEV_CSRF):

Double-submit only compares equal when both sides pick the same cookie, so wherever the jar holds both with different values the frontend submits one token and the backend validates against the other — every write 403s until the user clears cookies.

When both coexist. The backend sets only one (csrf_cookie_name(), :52, on cookie_secure()), so this is not a steady state — it is a transition. A browser holding atl_csrf from before ATL_COOKIE_SECURE/RENDER became truthy keeps it (the __Host- prefix forbids Domain, so the new cookie cannot overwrite the old name), and the server then issues __Host-atl_csrf beside it. :74 clears both, so a logout/login cycle resolves it — which is also why this can look intermittent and user-specific.

Fix: swap the array to ['__Host-atl_csrf', 'atl_csrf'] to match csrf.py:79.

Introduced by #285. The same defect was copied into dashboard/frontend/js/admin-shell.js and fixed there in #488, which deliberately leaves this call site alone — the two are independent copies, and this one is not that PR's to change.

A regression test needs to set both cookies to different values; the existing CSRF tests set one at a time and are structurally blind to ordering.

🤖 Filed with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions