MCPcopy
hub / github.com/gofiber/fiber / Test_Session_ExtractorPreservation

Function Test_Session_ExtractorPreservation

middleware/session/session_test.go:710–808  ·  view source on GitHub ↗

Test_Session_ExtractorPreservation verifies that the extractor which actually supplied the incoming session ID drives the write-back decision, preventing a session ID read from a read-only source (query/form/param) from being promoted into cookies/headers for an existing session (session fixation).

(t *testing.T)

Source from the content-addressed store, hash-verified

708// a session ID read from a read-only source (query/form/param) from being
709// promoted into cookies/headers for an existing session (session fixation).
710func Test_Session_ExtractorPreservation(t *testing.T) {
711 t.Parallel()
712
713 t.Run("read-only winner is not promoted for existing session", func(t *testing.T) {
714 t.Parallel()
715 // Chain prefers the cookie, but also accepts a read-only query source.
716 store := NewStore(Config{
717 Extractor: extractors.Chain(extractors.FromCookie("session_id"), extractors.FromQuery("session_id")),
718 })
719 app := fiber.New()
720
721 // First request: create and persist a session (written to the cookie).
722 ctx1 := app.AcquireCtx(&fasthttp.RequestCtx{})
723 sess, err := store.Get(ctx1)
724 require.NoError(t, err)
725 sess.Set("name", "john")
726 require.NoError(t, sess.Save())
727 id := sess.ID()
728 require.NotNil(t, ctx1.Response().Header.PeekCookie("session_id"))
729 sess.Release()
730 app.ReleaseCtx(ctx1)
731
732 // Second request: the existing ID is provided only via the query string
733 // (an attacker-controllable, read-only source).
734 ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
735 defer app.ReleaseCtx(ctx2)
736 ctx2.Request().SetRequestURI("/path?session_id=" + id)
737
738 sess2, err := store.Get(ctx2)
739 require.NoError(t, err)
740 require.False(t, sess2.Fresh(), "existing session must not be fresh")
741 require.Equal(t, id, sess2.ID())
742 require.NoError(t, sess2.Save())
743
744 // The read-only ID must NOT be promoted into a cookie/header.
745 require.Nil(t, ctx2.Response().Header.PeekCookie("session_id"))
746 require.Empty(t, string(ctx2.Response().Header.Peek("session_id")))
747 sess2.Release()
748 })
749
750 t.Run("fresh session still persists to writable sink", func(t *testing.T) {
751 t.Parallel()
752 store := NewStore(Config{
753 Extractor: extractors.Chain(extractors.FromCookie("session_id"), extractors.FromQuery("session_id")),
754 })
755 app := fiber.New()
756
757 // The query supplies an ID that does not exist in storage, so a fresh
758 // session with a freshly generated ID is created.
759 ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
760 defer app.ReleaseCtx(ctx)
761 ctx.Request().SetRequestURI("/path?session_id=does-not-exist")
762
763 sess, err := store.Get(ctx)
764 require.NoError(t, err)
765 require.True(t, sess.Fresh(), "missing data must yield a fresh session")
766 require.NotEqual(t, "does-not-exist", sess.ID(), "fresh session must use a generated ID")
767 require.NoError(t, sess.Save())

Callers

nothing calls this directly

Calls 15

GetMethod · 0.95
ChainFunction · 0.92
FromCookieFunction · 0.92
FromQueryFunction · 0.92
FromHeaderFunction · 0.92
NewStoreFunction · 0.85
AcquireCtxMethod · 0.80
ReleaseCtxMethod · 0.80
ContainsMethod · 0.80
NewMethod · 0.65
SetMethod · 0.65
ResponseMethod · 0.65

Tested by

no test coverage detected