Skip to content

Log the private session id so session rows arrive and can't leak session cookies - #30

Merged
PetrHeinz merged 2 commits into
mainfrom
claude/session-private-id
Oct 2, 2026
Merged

PetrHeinz merged 2 commits into
mainfrom
claude/session-private-id

Conversation

@PetrHeinz

Copy link
Copy Markdown
Member

Since Rack 1.6.12 and 2.0.8 (and in the rack-session gem Rack 3 uses), env["rack.session"].id is a Rack::Session::SessionId object, not a String. SessionContext puts that object into the context of every line logged during the request:

  • MessagePack can't encode it, so the HTTP device drops the whole batch, including lines from other requests. From the red-team of 0.2.8: with a session middleware in front, 0 of 30 rows arrived.
  • Its to_s and public_id are the session cookie value for server-side stores (Pool, Redis, Memcache). Wherever it gets serialised as a string, anyone who can read the logs can hijack those sessions.

What changes:

  • SessionContext logs id.private_id when the id responds to it. That is "2::" followed by the SHA-256 digest of the session id, the key Rack's server-side stores use, and it can't be turned back into a cookie.
  • Plain String ids, from Rack before 1.6.12/2.0.8 or custom session objects, are logged as before. On Rack 1.2 the session is a plain Hash without an id, so nothing changes there either.
  • The tests run Cookie and Pool sessions through SessionContext on every Rack in the CI matrix and check that the logged entries encode with MessagePack. They need rack-session in the Gemfile: Rack 3 moved the session middlewares there, and its 1.x releases for older Racks are empty shims.

Behaviour and compatibility:

  • With the HTTP device, rows logged under a session now arrive instead of being dropped.
  • Where the old value did get out (a JSON formatter writing to STDOUT), it was the public id. It is now the private id, so context.session.id of old and new rows won't match. The private id is stable for the life of the session, so lines can still be grouped by session.

Targets the logtail-rack 0.2.9 patch release. No dependencies on the other open PRs. Note that the logtail core change encoding unencodable values with to_s would turn this object into the cookie value, so the private id has to be picked here either way.

The first commit only adds the tests (and rack-session to the Gemfile) and is expected to fail on CI; the fix follows in the next commit.

🤖 Generated with Claude Code

PetrHeinz and others added 2 commits October 1, 2026 18:44
Since Rack 1.6.12 and 2.0.8 (and in rack-session), session.id is a
Rack::Session::SessionId. SessionContext logs that object: MessagePack
can't encode it, so the HTTP device drops the whole batch, and its
public id is the cookie value of server-side stores like Pool.

The tests use Cookie and Pool sessions, so the Gemfile gets rack-session
(Rack 3 moved the session middlewares there; its 1.x is an empty shim).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SessionContext logs id.private_id when the session id responds to it:
"2::" and the SHA-256 digest of the session id, the key Rack's
server-side stores use. It is a String, so MessagePack can encode the
batch again, and unlike the public id it isn't the session cookie of
Pool, Redis or Memcache stores. Plain String ids from older Racks are
logged as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PetrHeinz
PetrHeinz marked this pull request as ready for review October 2, 2026 09:19
@PetrHeinz
PetrHeinz merged commit 4ca0d65 into main Oct 2, 2026
24 checks passed
@PetrHeinz
PetrHeinz deleted the claude/session-private-id branch October 2, 2026 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant