You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replaces the implicit "preserve query/form/param/custom session IDs" behaviour
with an explicit, validated opt-in and centralizes source tracking so chained
extractors stay consistent across multiple Store.Get calls in the same request.
Changes:
- extractors: add Source.IsWritable() so callers no longer hand-roll the
cookie/header check.
- session.Store: cache the resolved session ID together with its originating
Source in request locals (sessionIDInfo). resolveSessionID iterates chain
sub-extractors and reports the source that actually produced the value;
subsequent Store.Get calls in the same request reuse that decision instead
of re-deriving it from the chain wrapper.
- session.Config: add TrustClientSessionID (default false) and
ClientSessionIDValidator. Read-only sources (query/form/param/custom) only
preserve a client-supplied unknown ID when both flags are set and the
validator accepts it; otherwise the ID is discarded and a fresh server ID
is generated, matching cookie/header fixation protection. Cookie/header
sources are unaffected and always discard unknown IDs.
- docs/middleware/session.md: document the new flags, the security
trade-offs, and the recommended HMAC/allow-list validator pattern.
- store_test.go: update Test_Store_resolveSessionID to the new signature,
cover trust-on/off, validator rejects, opt-in roundtrip, chain source
resolution, two-Get-same-request consistency for both query and cookie
sources, and empty client ID. Test_Store_DeleteSession now models the
cross-request delete + re-Get path instead of relying on the previous
regenerate-loop bug.
Closes#4234
Copy file name to clipboardExpand all lines: docs/middleware/session.md
+33Lines changed: 33 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -369,6 +369,37 @@ extractors.Chain(
369
369
)
370
370
```
371
371
372
+
### Trusting Client-Supplied IDs from Read-Only Sources
373
+
374
+
By default, an unknown session ID from any source is discarded and a new one is generated via `KeyGenerator`. For cookie/header sources that is also the response channel for the new ID, so the next request continues with it. Read-only sources (query, form, URL param, custom extractors) cannot communicate a new ID back, so the same client request would otherwise create a new orphan session every time.
375
+
376
+
If your application needs read-only sources to drive a persistent session — for example a non-browser client that always sends the same `?SESSIONID=...` — opt in explicitly:
377
+
378
+
```go
379
+
app.Use(session.New(session.Config{
380
+
Extractor: extractors.FromQuery("SESSIONID"),
381
+
TrustClientSessionID: true,
382
+
ClientSessionIDValidator: func(id string) bool {
383
+
// Verify the format/origin of the ID. Reject anything you did not issue.
**Security implications.** Trusting client-supplied IDs without validation enables:
391
+
392
+
-**Session fixation** — an attacker can craft a link such as `?SESSIONID=ATTACKER_KNOWN_VALUE`; once the victim follows it, the server creates a session under that ID and the attacker can hijack it.
393
+
-**Storage poisoning** — any caller can populate your session storage with arbitrary keys.
394
+
395
+
Mitigations:
396
+
397
+
1. Always supply a `ClientSessionIDValidator` that rejects IDs you did not issue (HMAC signature, registered allow-list, signed JWT, etc.).
398
+
2. Combine the read-only source with a server-issued token bootstrap step.
399
+
3. Prefer cookie or header extractors whenever the client can store them.
400
+
401
+
Cookie and header sources are unaffected by this flag — their unknown IDs are always discarded to prevent fixation.
402
+
372
403
### Custom Extractors (Session-specific)
373
404
374
405
Prefer the helper constructors from the extractors module. See the Extractors Guide for the full API; below are session-specific examples and notes.
|`Store`|`*session.Store`| Pre-built session store (use when you need to share/register types) |`nil` (auto-created) |
682
713
|`Storage`|`fiber.Storage`| Session storage backend (used when creating a store if `Store` is nil) |`memory.New()`|
683
714
|`Extractor`|`extractors.Extractor`| Session ID extraction |`extractors.FromCookie("session_id")`|
715
+
|`TrustClientSessionID`|`bool`| Accept client-supplied IDs from read-only sources (query/form/param/custom) when no data exists. Requires `ClientSessionIDValidator`. |`false`|
716
+
|`ClientSessionIDValidator`|`func(string) bool`| Validates a client-supplied session ID before persisting it. Required when `TrustClientSessionID` is `true`; `nil` rejects all. |`nil`|
684
717
|`KeyGenerator`|`func() string`| Session ID generator |`utils.SecureToken`|
0 commit comments