Skip to content

fix(session): add rotate_session_id() against session fixation at login - #255

Merged
allen0099 merged 1 commit into
masterfrom
fix/session-fixation-225
Sep 26, 2026
Merged

allen0099 merged 1 commit into
masterfrom
fix/session-fixation-225

Conversation

@allen0099

@allen0099 allen0099 commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Closes #225.

Problem

Under FastAPICacheXSessionMiddleware the cookie only names a server-side record. A Starlette-style login that writes request.session["user_id"] = ... keeps the session ID the request arrived with. As a result, a planted cookie is logged in along with the victim. The documented defence (SESSION.md §5) took SessionDep, which answers 401 to a new visitor, so it could not be used as written.

Change

  • New rotate_session_id(request) -> bool in fastapi_cachex.session.dependencies, also exported from fastapi_cachex.session.
    • If a session was loaded, it calls SessionManager.regenerate_session_id() on it. The middlewares already notice the changed ID and send the new token through the request's transport: Set-Cookie, or the response header.
    • With no session loaded, it returns False and does nothing. The first write then starts a session under a fresh ID.
  • Docs (EN and zh-TW)
    • §5 now uses the helper.
    • It explains the get_optional_session alternative for handlers that call regenerate_session_id() directly.
    • The request.session migration section warns about the difference from Starlette.
    • The helper is listed with the session dependencies.
  • CHANGELOG gets a new ### Security entry under Unreleased.
  • CLAUDE.md gets a line about the helper.

I left out the automatic "rotate when a user is first attached" config switch the issue mentions. The middleware cannot tell a login from any other write to request.session, so an explicit call is the reliable option. The breaking version of that idea (an explicit login()/logout() that always rotates, a __Host- cookie by default) is tracked for 0.4.0 in #256; rotate_session_id() stays useful there for privilege changes.

Tests

tests/session/test_starlette_middleware.py:

  • test_rotate_session_id_defeats_a_planted_cookie replays the issue's repro:
    • the victim gets a new token;
    • the data (cart plus user_id) is kept;
    • the planted token raises SessionNotFoundError.
  • test_rotate_session_id_without_a_session: a new visitor gets 200, rotated: False, and a session cookie.
  • test_rotate_session_id_over_the_header: the header transport returns the new token in the header, with no Set-Cookie.

Mutation check: with the regenerate_session_id() call removed from the helper, exactly the two rotation tests fail. The no-session test guards the 401 regression, not the rotation.

Local results:

  • ruff check and ruff format --check pass.
  • mypy --strict passes.
  • Full pytest: 731 passed. The 182 skipped are the live Redis/Memcached tests, and this change does not touch the backends.
  • Both docs builds pass --strict --clean.

A Starlette-style login under FastAPICacheXSessionMiddleware writes into
the session the request arrived with and sends the same token back, so
whoever planted that cookie is logged in too. rotate_session_id(request)
regenerates the loaded session's ID (a no-op when none was loaded) and
the middleware sends the new token through the request's transport.

The SESSION.md example used SessionDep and answered 401 to new
visitors; it now uses the helper, and the migration section explains
the difference from Starlette's cookie sessions.

Closes #225
@allen0099 allen0099 added this to the 0.3.8 milestone Sep 26, 2026
@allen0099 allen0099 added bug Something isn't working session Session management subsystem labels Sep 26, 2026
@allen0099
allen0099 merged commit a3d983c into master Sep 26, 2026
11 checks passed
@allen0099
allen0099 deleted the fix/session-fixation-225 branch September 26, 2026 21:23
@allen0099 allen0099 added the security Security vulnerability or hardening label Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working security Security vulnerability or hardening session Session management subsystem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Session fixation in cookie mode: a login keeps the pre-login session ID

1 participant