Skip to content

Commit

Permalink
Merge pull request from GHSA-98j2-3j3p-fw2v
Browse files Browse the repository at this point in the history
* fix: token injection vulnerability GHSA-98j2-3j3p-fw2v

- Ensure session IDs are securely generated server-side.
- Add validation to prevent user-supplied session IDs.
- Update tests to verify correct session token use.

This update addresses the critical session middleware vulnerability identified in versions 2 and above of GoFiber.

* test(middleware/csrf): Save session after generating new session ID

This commit saves the session after generating a new session ID to ensure that the updated session ID is persisted. This change is necessary to address a critical session middleware vulnerability identified in versions 2 and above of GoFiber.

* chore: Save session ID in context for middleware chain

The code changes add functionality to save the newly generated session ID in the context, allowing it to be accessible to subsequent middlewares in the chain. This improvement ensures that the session ID is available for use throughout the middleware stack.

* test: Fix session freshness check in session_test

The code changes in `session_test.go` fix the session freshness check by updating the assertions for `sess.Fresh()` and `sess.ID()`. The previous assertions were incorrect and have been corrected to ensure the session ID remains the same and the session is not fresh.

* refactor(session.go): general clean-up

* chore: Revert session freshness behavior

The code changes in `session_test.go` fix the session freshness check by updating the assertions for `sess.Fresh()` and `sess.ID()`. The previous assertions were incorrect and have been corrected to ensure the session ID remains the same and the session is not fresh.
  • Loading branch information
sixcolors authored Jun 26, 2024
1 parent 4262f5b commit 7926e5b
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 74 deletions.
4 changes: 4 additions & 0 deletions middleware/csrf/csrf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ func Test_CSRF_WithSession(t *testing.T) {

// the session string is no longer be 123
newSessionIDString := sess.ID()
sess.Save()

app.AcquireCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString)

// middleware config
Expand Down Expand Up @@ -221,6 +223,8 @@ func Test_CSRF_ExpiredToken_WithSession(t *testing.T) {

// get session id
newSessionIDString := sess.ID()
sess.Save()

app.AcquireCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString)

// middleware config
Expand Down
95 changes: 84 additions & 11 deletions middleware/session/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,23 @@ func Test_Session(t *testing.T) {
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(ctx)

// Get a new session
sess, err := store.Get(ctx)
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, true, sess.Fresh())
token := sess.ID()
sess.Save()

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})

// set session
ctx.Request().Header.SetCookie(store.sessionName, "123")
ctx.Request().Header.SetCookie(store.sessionName, token)

// get session
sess, err := store.Get(ctx)
sess, err = store.Get(ctx)
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, true, sess.Fresh())
utils.AssertEqual(t, false, sess.Fresh())

// get keys
keys := sess.Keys()
Expand Down Expand Up @@ -64,12 +74,14 @@ func Test_Session(t *testing.T) {

// get id
id := sess.ID()
utils.AssertEqual(t, "123", id)
utils.AssertEqual(t, token, id)

// save the old session first
err = sess.Save()
utils.AssertEqual(t, nil, err)

app.ReleaseCtx(ctx)

// requesting entirely new context to prevent falsy tests
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(ctx)
Expand Down Expand Up @@ -108,7 +120,6 @@ func Test_Session_Types(t *testing.T) {

// fiber context
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(ctx)

// set cookie
ctx.Request().Header.SetCookie(store.sessionName, "123")
Expand All @@ -120,6 +131,10 @@ func Test_Session_Types(t *testing.T) {

// the session string is no longer be 123
newSessionIDString := sess.ID()

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})

ctx.Request().Header.SetCookie(store.sessionName, newSessionIDString)

type User struct {
Expand Down Expand Up @@ -177,6 +192,11 @@ func Test_Session_Types(t *testing.T) {
err = sess.Save()
utils.AssertEqual(t, nil, err)

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})

ctx.Request().Header.SetCookie(store.sessionName, newSessionIDString)

// get session
sess, err = store.Get(ctx)
utils.AssertEqual(t, nil, err)
Expand All @@ -203,6 +223,7 @@ func Test_Session_Types(t *testing.T) {
utils.AssertEqual(t, vfloat64, sess.Get("vfloat64").(float64))
utils.AssertEqual(t, vcomplex64, sess.Get("vcomplex64").(complex64))
utils.AssertEqual(t, vcomplex128, sess.Get("vcomplex128").(complex128))
app.ReleaseCtx(ctx)
}

// go test -run Test_Session_Store_Reset
Expand All @@ -214,7 +235,6 @@ func Test_Session_Store_Reset(t *testing.T) {
app := fiber.New()
// fiber context
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(ctx)

// get session
sess, err := store.Get(ctx)
Expand All @@ -228,6 +248,12 @@ func Test_Session_Store_Reset(t *testing.T) {

// reset store
utils.AssertEqual(t, nil, store.Reset())
id := sess.ID()

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(ctx)
ctx.Request().Header.SetCookie(store.sessionName, id)

// make sure the session is recreated
sess, err = store.Get(ctx)
Expand Down Expand Up @@ -302,25 +328,37 @@ func Test_Session_Save_Expiration(t *testing.T) {
// set value
sess.Set("name", "john")

token := sess.ID()

// expire this session in 5 seconds
sess.SetExpiry(sessionDuration)

// save session
err = sess.Save()
utils.AssertEqual(t, nil, err)

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})

// here you need to get the old session yet
ctx.Request().Header.SetCookie(store.sessionName, token)
sess, err = store.Get(ctx)
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, "john", sess.Get("name"))

// just to make sure the session has been expired
time.Sleep(sessionDuration + (10 * time.Millisecond))

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(ctx)

// here you should get a new session
ctx.Request().Header.SetCookie(store.sessionName, token)
sess, err = store.Get(ctx)
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, nil, sess.Get("name"))
utils.AssertEqual(t, true, sess.ID() != token)
})
}

Expand Down Expand Up @@ -364,7 +402,15 @@ func Test_Session_Destroy(t *testing.T) {

// set value & save
sess.Set("name", "fenny")
id := sess.ID()
utils.AssertEqual(t, nil, sess.Save())

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(ctx)

// get session
ctx.Request().Header.Set(store.sessionName, id)
sess, err = store.Get(ctx)
utils.AssertEqual(t, nil, err)

Expand Down Expand Up @@ -408,7 +454,8 @@ func Test_Session_Cookie(t *testing.T) {
}

// go test -run Test_Session_Cookie_In_Response
func Test_Session_Cookie_In_Response(t *testing.T) {
// Regression: https://github.com/gofiber/fiber/pull/1191
func Test_Session_Cookie_In_Middleware_Chain(t *testing.T) {
t.Parallel()
store := New()
app := fiber.New()
Expand All @@ -421,15 +468,17 @@ func Test_Session_Cookie_In_Response(t *testing.T) {
sess, err := store.Get(ctx)
utils.AssertEqual(t, nil, err)
sess.Set("id", "1")
id := sess.ID()
utils.AssertEqual(t, true, sess.Fresh())
utils.AssertEqual(t, nil, sess.Save())

sess, err = store.Get(ctx)
utils.AssertEqual(t, nil, err)
sess.Set("name", "john")
utils.AssertEqual(t, true, sess.Fresh())
utils.AssertEqual(t, id, sess.ID()) // session id should be the same

utils.AssertEqual(t, "1", sess.Get("id"))
utils.AssertEqual(t, sess.ID() != "1", true)
utils.AssertEqual(t, "john", sess.Get("name"))
}

Expand All @@ -441,24 +490,31 @@ func Test_Session_Deletes_Single_Key(t *testing.T) {
app := fiber.New()

ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(ctx)

sess, err := store.Get(ctx)
utils.AssertEqual(t, nil, err)
ctx.Request().Header.SetCookie(store.sessionName, sess.ID())

id := sess.ID()
sess.Set("id", "1")
utils.AssertEqual(t, nil, sess.Save())

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})
ctx.Request().Header.SetCookie(store.sessionName, id)

sess, err = store.Get(ctx)
utils.AssertEqual(t, nil, err)
sess.Delete("id")
utils.AssertEqual(t, nil, sess.Save())

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})
ctx.Request().Header.SetCookie(store.sessionName, id)

sess, err = store.Get(ctx)
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, false, sess.Fresh())
utils.AssertEqual(t, nil, sess.Get("id"))
app.ReleaseCtx(ctx)
}

// go test -run Test_Session_Reset
Expand All @@ -475,6 +531,9 @@ func Test_Session_Reset(t *testing.T) {
defer app.ReleaseCtx(ctx)

t.Run("reset session data and id, and set fresh to be true", func(t *testing.T) {
t.Parallel()
// fiber context
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
// a random session uuid
originalSessionUUIDString := ""

Expand All @@ -491,6 +550,9 @@ func Test_Session_Reset(t *testing.T) {
err = freshSession.Save()
utils.AssertEqual(t, nil, err)

app.ReleaseCtx(ctx)
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})

// set cookie
ctx.Request().Header.SetCookie(store.sessionName, originalSessionUUIDString)

Expand Down Expand Up @@ -524,6 +586,8 @@ func Test_Session_Reset(t *testing.T) {
// Check that the session id is not in the header or cookie anymore
utils.AssertEqual(t, "", string(ctx.Response().Header.Peek(store.sessionName)))
utils.AssertEqual(t, "", string(ctx.Request().Header.Peek(store.sessionName)))

app.ReleaseCtx(ctx)
})
}

Expand Down Expand Up @@ -551,6 +615,12 @@ func Test_Session_Regenerate(t *testing.T) {
err = freshSession.Save()
utils.AssertEqual(t, nil, err)

// release the context
app.ReleaseCtx(ctx)

// acquire a new context
ctx = app.AcquireCtx(&fasthttp.RequestCtx{})

// set cookie
ctx.Request().Header.SetCookie(store.sessionName, originalSessionUUIDString)

Expand All @@ -566,6 +636,9 @@ func Test_Session_Regenerate(t *testing.T) {

// acquiredSession.fresh should be true after regenerating
utils.AssertEqual(t, true, acquiredSession.Fresh())

// release the context
app.ReleaseCtx(ctx)
})
}

Expand Down
Loading

0 comments on commit 7926e5b

Please sign in to comment.