Skip to content

🐛 bug: prevent session fixation by preserving successful extractor in chains - #4469

Merged
ReneWerner87 merged 4 commits into
mainfrom
fix-session-id-persistence-vulnerability
Jun 29, 2026
Merged

ReneWerner87 merged 4 commits into
mainfrom
fix-session-id-persistence-vulnerability

Conversation

@gaby

@gaby gaby commented Jun 28, 2026

Copy link
Copy Markdown
Member

Motivation

  • Fix a session-fixation risk where a read-only extractor (query/form/param) used in a chained extractor could be promoted into writable sinks (cookies/headers) when the chain also contained cookie/header extractors.
  • Preserve the original behavior for freshly generated sessions while preventing promotion of attacker-controlled IDs extracted from read-only sources.

Description

  • Record the specific extractor that successfully supplied the incoming session ID by adding a context-local key (sessionExtractorContextKey) and capturing it during extraction in Store.getSessionID. (modified middleware/session/store.go)
  • Attach the selected extractor to the in-memory Session (sess.extractor) when a session is created so later save/delete operations can make write-back decisions based on the actual source. (modified middleware/session/store.go, middleware/session/session.go)
  • Change Session.getExtractorInfo() to prefer the stored successful extractor when it is a cookie/header sink and otherwise fall back to the configured cookie/header extractors for fresh sessions. (modified middleware/session/session.go)
  • Reset the stored extractor on Session.Release() to avoid cross-request contamination from the session pool. (modified middleware/session/session.go)

Testing

  • Ran targeted session unit tests: go test ./middleware/session -run 'Test_Session_Save|Test_Session_ChainedExtractors|Test_Store_getSessionID' which passed for the modified behavior (example output: ok github.com/gofiber/fiber/v3/middleware/session 5.129s).
  • Verified extraction selection behavior with go test ./middleware/session -run Test_Store_getSessionID (passed).
  • Executed repository checks per AGENTS.md: make generate succeeded, make modernize succeeded, and make format succeeded, while make audit failed due to environment-reported standard-library vuln findings from govulncheck (pre-existing toolchain/stdlib issues unrelated to this patch) and make betteralign reported a non-functional struct-alignment suggestion; these failures are environmental/pre-existing and did not affect the session-targeted tests.
  • Ran the broader make test (full test suite); the session-targeted tests relevant to this fix passed, though unrelated integration tests in the full shuffled/race run surfaced unrelated failures in this environment.

Codex Task

@gaby
gaby requested a review from a team as a code owner June 28, 2026 12:03
@gaby
gaby requested a review from sixcolors June 28, 2026 12:03
@gaby
gaby requested review from ReneWerner87 and efectn June 28, 2026 12:03
@ReneWerner87 ReneWerner87 added this to v3 Jun 28, 2026
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8d795c91-743d-471b-bc30-468a007f9dcd

📥 Commits

Reviewing files that changed from the base of the PR and between c3b467e and e7ac90c.

📒 Files selected for processing (4)
  • AGENTS.md
  • middleware/session/session.go
  • middleware/session/session_test.go
  • middleware/session/store.go
✅ Files skipped from review due to trivial changes (1)
  • AGENTS.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • middleware/session/session.go
  • middleware/session/store.go

Walkthrough

Session now stores the extractor that produced its session ID. Session ID extraction records the selected extractor in context locals, getSession attaches it to the session, and getExtractorInfo prefers that stored extractor when it is cookie- or header-based.

Changes

Extractor tracking through getSessionID → Session

Layer / File(s) Summary
Session struct field and pool reset
middleware/session/session.go
Adds extractor to Session and replaces isFresh with fresh; acquire, refresh, release, and Fresh() use the new field names.
getSessionID chain iteration and extractor recording
middleware/session/store.go
Adds sessionExtractorContextKey, records the extractor that produced a non-empty session ID, loads that extractor into sess.extractor, and uses fresh when creating or loading sessions by ID.
getExtractorInfo prefers per-session extractor
middleware/session/session.go
Returns sess.extractor for cookie/header sources when present; otherwise filters the configured extractor for cookie/header sources or returns the default cookie extractor when config is nil.
Tests and lint guidance
middleware/session/session_test.go, AGENTS.md
Adds session tests for extractor preservation and getExtractorInfo behavior, and adds lint guidance for errcheck and type-assertion handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • gofiber/fiber#3625: Adds extractor chaining in middleware/session, which this PR extends by recording the winning extractor.
  • gofiber/fiber#3825: Also adjusts session freshness handling in middleware/session/store.go and updates tests around session reuse.
  • gofiber/fiber#3725: Builds on shared extractors behavior used by session handling, including extractor source and chain semantics.

Suggested labels

SessionMW

Suggested reviewers

  • efectn
  • ReneWerner87

Poem

🐇 A hop, a skip, a cookie gleam,
The winning extractor joins the stream.
In locals tucked, then back to the sess,
Header or cookie? We now know best.
Clean pool, fresh path, all spry and light. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately describes the main change: preventing session fixation by preserving the successful extractor in chains.
Description check ✅ Passed The description covers motivation, implementation details, and testing; only template extras like Fixes # and checklist items are missing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-session-id-persistence-vulnerability

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ReneWerner87 ReneWerner87 added this to the v3 milestone Jun 28, 2026
@gaby gaby removed the aardvark label Jun 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@middleware/session/session.go`:
- Around line 380-399: The extractor selection logic is still falling through
from a read-only winner into writable extractors, which lets `setSession`
persist the same session ID into cookies/headers. Update the extractor
resolution in this block so that once `s.extractor` has won with a non-writable
source (for example query/form/param), you do not continue to
`s.config.Extractor` or its `Chain`; only return cookie/header extractors when
the winning extractor itself is cookie/header, and otherwise stop with no
writable fallback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 62773ff8-151b-41b4-8fb6-2c9407588a7e

📥 Commits

Reviewing files that changed from the base of the PR and between fe33129 and 7a28eab.

📒 Files selected for processing (2)
  • middleware/session/session.go
  • middleware/session/store.go

Comment thread middleware/session/session.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@middleware/session/session.go`:
- Line 64: The Session field rename was only partially applied: update all
remaining references in Session methods to use s.fresh instead of s.isFresh. Fix
the assignments/checks in the Session-related code paths that currently
reference the old name (including the places in the Session type’s methods) so
they match the renamed struct field and the existing sess.fresh usage in
store.go.

In `@middleware/session/store.go`:
- Line 318: The session store code still references the removed `sess.isFresh`
field, causing a compile error. Update the `store.go` logic to use the renamed
`sess.fresh` field wherever this freshness flag is reset, matching the existing
assignment in the session handling code so the store and session structs stay
consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 883b943b-ba59-4626-92ac-6ddd6b07d849

📥 Commits

Reviewing files that changed from the base of the PR and between 7a28eab and 0a72608.

📒 Files selected for processing (2)
  • middleware/session/session.go
  • middleware/session/store.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@middleware/session/session.go`:
- Line 64: The Session field rename was only partially applied: update all
remaining references in Session methods to use s.fresh instead of s.isFresh. Fix
the assignments/checks in the Session-related code paths that currently
reference the old name (including the places in the Session type’s methods) so
they match the renamed struct field and the existing sess.fresh usage in
store.go.

In `@middleware/session/store.go`:
- Line 318: The session store code still references the removed `sess.isFresh`
field, causing a compile error. Update the `store.go` logic to use the renamed
`sess.fresh` field wherever this freshness flag is reset, matching the existing
assignment in the session handling code so the store and session structs stay
consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 883b943b-ba59-4626-92ac-6ddd6b07d849

📥 Commits

Reviewing files that changed from the base of the PR and between 7a28eab and 0a72608.

📒 Files selected for processing (2)
  • middleware/session/session.go
  • middleware/session/store.go
🛑 Comments failed to post (2)
middleware/session/session.go (1)

64-64: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Build break: s.isFresh no longer exists — rename to s.fresh.

The struct field was renamed from isFresh to fresh (and store.go Line 165 already assigns sess.fresh), but these references were not updated. The compiler reports s.isFresh undefined (type *Session has no field or method isFresh) at Lines 64, 117, and 326, failing lint, govulncheck, and all unit jobs.

🐛 Proposed fix
@@ acquireSession (Line 64)
-	s.isFresh = true
+	s.fresh = true
@@ Fresh (Line 117)
-	return s.isFresh
+	return s.fresh
@@ refresh (Line 326)
-	s.isFresh = true
+	s.fresh = true

Also applies to: 117-117, 326-326

🧰 Tools
🪛 GitHub Actions: Linter / 0_lint _ lint.txt

[error] 64-64: golangci-lint: undefined method/field 'isFresh' (s.isFresh undefined). Error: (type *Session has no field or method isFresh).

🪛 GitHub Actions: Linter / lint _ lint

[error] 64-64: golangci-lint reported compile error: s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Actions: PR `#4469` / 0_Analyze (go).txt

[warning] 64-64: Type extraction warning while processing package github.com/gofiber/fiber/v3/middleware/session: /home/runner/work/fiber/fiber/middleware/session/session.go:64:4: s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Actions: PR `#4469` / Analyze (go)

[error] 64-64: Type extraction failed while processing package github.com/gofiber/fiber/v3/middleware/session: /home/runner/work/fiber/fiber/middleware/session/session.go:64:4: s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Actions: Run govulncheck / 0_govulncheck-check.txt

[error] 64-64: govulncheck failed due to Go compilation issue: s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Actions: Run govulncheck / govulncheck-check

[error] 64-64: govulncheck failed due to package loading error: /home/runner/work/fiber/fiber/middleware/session/session.go:64:4: s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: govulncheck-check

[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: lint / lint

[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: unit (1.25.x, macos-latest)

[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)


[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: unit (1.25.x, ubuntu-latest)

[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)


[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: unit (1.25.x, windows-latest)

[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)


[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: unit (1.26.x, macos-latest)

[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)


[failure] 64-64:
s.isFresh undefined (type *Session has no field or method isFresh)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@middleware/session/session.go` at line 64, The Session field rename was only
partially applied: update all remaining references in Session methods to use
s.fresh instead of s.isFresh. Fix the assignments/checks in the Session-related
code paths that currently reference the old name (including the places in the
Session type’s methods) so they match the renamed struct field and the existing
sess.fresh usage in store.go.

Source: Linters/SAST tools

middleware/session/store.go (1)

318-318: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Build break: sess.isFresh no longer exists — rename to sess.fresh.

Same root cause as the session.go rename: the field is now fresh (assigned as sess.fresh at Line 165), so this reference fails to compile (sess.isFresh undefined).

🐛 Proposed fix
-	sess.isFresh = false
+	sess.fresh = false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

	sess.fresh = false
🧰 Tools
🪛 GitHub Check: govulncheck-check

[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: lint / lint

[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh) (typecheck)

🪛 GitHub Check: unit (1.25.x, macos-latest)

[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)


[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: unit (1.25.x, ubuntu-latest)

[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)


[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: unit (1.25.x, windows-latest)

[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)


[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)

🪛 GitHub Check: unit (1.26.x, macos-latest)

[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)


[failure] 318-318:
sess.isFresh undefined (type *Session has no field or method isFresh)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@middleware/session/store.go` at line 318, The session store code still
references the removed `sess.isFresh` field, causing a compile error. Update the
`store.go` logic to use the renamed `sess.fresh` field wherever this freshness
flag is reset, matching the existing assignment in the session handling code so
the store and session structs stay consistent.

Source: Linters/SAST tools

The merge of main introduced the isFresh -> fresh struct field rename, but
the session-extractor changes still referenced the old isFresh name, leaving
the package uncompilable. Update the remaining references in session.go and
store.go to use the new fresh field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSaJqSGCPXEHywa3bhWJnT
@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.92%. Comparing base (fe33129) to head (e7ac90c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4469      +/-   ##
==========================================
+ Coverage   92.89%   92.92%   +0.02%     
==========================================
  Files         138      138              
  Lines       13486    13509      +23     
==========================================
+ Hits        12528    12553      +25     
+ Misses        591      590       -1     
+ Partials      367      366       -1     
Flag Coverage Δ
unittests 92.92% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Address the review feedback on the session-fixation fix:

- getExtractorInfo no longer falls through to writable cookie/header sinks
  when the winning extractor was a read-only source (query/form/param) for an
  existing session, which closed the session-fixation path the PR targets.
  Freshly generated sessions still persist to the configured sinks.
- Use the comma-ok form when reading the selected extractor from context
  locals so it satisfies the repo's strict errcheck config
  (check-type-assertions + check-blank), fixing the lint workflow failure.
- Add tests covering extractor preservation, the no-promotion path, fresh
  fallthrough, and getExtractorInfo branches (package coverage 92.8% -> 96.4%).
- Document the errcheck type-assertion/blank pitfalls in AGENTS.md so future
  changes run `make lint` and avoid the recurring failure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSaJqSGCPXEHywa3bhWJnT
@gaby

gaby commented Jun 28, 2026

Copy link
Copy Markdown
Member Author

Pending @sixcolors approval

@ReneWerner87
ReneWerner87 merged commit 60eaef3 into main Jun 29, 2026
27 of 28 checks passed
@ReneWerner87
ReneWerner87 deleted the fix-session-id-persistence-vulnerability branch June 29, 2026 06:30
@github-project-automation github-project-automation Bot moved this to Done in v3 Jun 29, 2026
ReneWerner87 added a commit that referenced this pull request Jun 29, 2026
PR #4469 renamed the unexported boolean field Session.isFresh (and the
local isFresh in Store.getSession) to fresh, dropping the predicate prefix
that signals the value is a boolean. Restore isFresh everywhere it was
shortened; the public Fresh() method is unchanged, so there is no API change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ReneWerner87 ReneWerner87 removed this from the v3 milestone Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants