-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] Recreating session (cookie) with securecookie enabled passes plain cookie value sometimes #1929
Comments
I haven't found the code where this happens but my rough guess: |
Hello @mblaschke, In your code example you initialize the session on each handler through: app.Use(sess.Handler()) Then, on your |
i reused the example from #1877 should i avoid using?
I tried to verify the issue why sometimes sessions are dropped (still working on an example) so i found the issue behind it that plain text session values are passed to securecookie. |
You should use ( SecureCookie.Encode: name = session_id_cookie, value = "6ef75d2a-adba-4c77-a9e9-645548786364"
SecureCookie.Decode: name = session_id_cookie, value = MTY1Nzk5MTI1N3xwOUdRbDBRS2NWbWVFcmZCcTdfU3REWDEzYmp5Tm83VTRmbko4eDFpSnd4SjBZclJHYzI2VXMybmpFVmRzY2JlREhOMTNWU2R4ZU09fFR9KtqAGcJRW_xQJSmr2pkFjyfxAE-64q2SSYdM582w
SecureCookie.Decode: name = session_id_cookie, value = MTY1Nzk5MTI1N3xwOUdRbDBRS2NWbWVFcmZCcTdfU3REWDEzYmp5Tm83VTRmbko4eDFpSnd4SjBZclJHYzI2VXMybmpFVmRzY2JlREhOMTNWU2R4ZU09fFR9KtqAGcJRW_xQJSmr2pkFjyfxAE-64q2SSYdM582w |
@mblaschke the code part, the securecookie's Decode is called is at: Lines 5314 to 5318 in cf42f37
If the decode is failed, the cookie value is set to empty string for security reasons. This may be happening if the previous Encode is not called for some reason, probably because you call Destroy and Start in the same handler for the same request-response lifecycle. |
@mblaschke this is a working example which destroys and re-initializes the session in the same request-response lifecycle (I don't recommend it) but here you are: package main
import (
"fmt"
"net/http"
"github.com/gorilla/securecookie"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/sessions"
)
const cookieNameForSessionID = "session_id_cookie"
func secret(ctx iris.Context) {
session := getSession(ctx, false)
// Check if user is authenticated
if auth, _ := session.GetBoolean("authenticated"); !auth {
ctx.StatusCode(iris.StatusForbidden)
ctx.ContentType("text/html")
ctx.WriteString(`Not logged in or session invalid <a href="/logout">logout</a>`)
return
}
// Print secret message
ctx.ContentType("text/html")
ctx.WriteString(`The cake is a lie! <a href="/logout">logout</a>`)
}
func login(ctx iris.Context) {
// Authentication goes here
// ...
// Set user as authenticated
session := getSession(ctx, true)
session.Set("authenticated", true)
ctx.StatusCode(http.StatusTemporaryRedirect)
ctx.ContentType("text/html")
ctx.Header("Location", "/secret")
}
func logout(ctx iris.Context) {
sessions.Get(ctx).Destroy()
ctx.ContentType("text/html")
ctx.WriteString(`<a href="/login">login</a>`)
}
func getSession(ctx iris.Context, recreate bool) *sessions.Session {
// cookieOptionList := []context.CookieOption{
// func(ctx *context.Context, cookie *http.Cookie, op uint8) {
// // c.applySessionSetting(ctx, cookie, op)
// },
// }
if recreate {
sessions.Get(ctx).Destroy()
fmt.Println("getSession.recreate: destroy called")
}
// s := sess.Start(ctx, cookieOptionList...)
// return s
return sessions.Get(ctx)
}
func main() {
app := iris.New()
secureCookie := securecookie.New(
[]byte("IGVneS5eZ0b5jTgwe0l38nON0bW5awxr"),
[]byte("r4GFiLvBtYCohUxt"),
)
sess := sessions.New(sessions.Config{
Cookie: cookieNameForSessionID,
Encoding: secureCookie,
CookieSecureTLS: false,
AllowReclaim: true,
DisableSubdomainPersistence: true,
})
app.Use(sess.Handler())
app.Get("/secret", secret)
app.Get("/login", login)
app.Get("/logout", logout)
app.Listen(":8080")
} It's the same but you use |
I found the origin of the "issue". This is happening because the |
I can confirm the session id is not send back to the client but the session is invalidated at that point. The reason for recreation of session is https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html#renew-the-session-id-after-any-privilege-level-change Also I want to change cookies samesite mode from lax (for oauth login) to strict (for application). |
Hello @mblaschke,
That's easy, you can do it using a middleware (for all cookies) and cookie options via var strictSameSiteOpt = func(_ iris.Context, c *http.Cookie, op uint8) {
if op == 1 /* context.OpCookieSet */ {
c.SameSite = http.SameSiteStrictMode
}
} sess := sessions.New(sessions.Config{
Cookie: cookieNameForSessionID,
Encoding: secureCookie,
CookieSecureTLS: false,
AllowReclaim: true,
DisableSubdomainPersistence: true,
})
app.Use(sess.Handler(strictSameSiteOpt))
Can you run this and provide some feedback? It works here: package main
import (
"github.com/gorilla/securecookie"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/sessions"
)
var strictSameSiteOpt = func(_ iris.Context, c *http.Cookie, op uint8) {
if op == 1 /* context.OpCookieSet */ {
c.SameSite = http.SameSiteStrictMode
}
}
var sessManager *sessions.Sessions
const sessionCookieName = "session_id"
func main() {
app := iris.New()
secureCookie := securecookie.New(
[]byte("IGVneS5eZ0b5jTgwe0l38nON0bW5awxr"),
[]byte("r4GFiLvBtYCohUxt"),
)
sessManager = sessions.New(sessions.Config{
Cookie: sessionCookieName,
Encoding: secureCookie,
CookieSecureTLS: false,
AllowReclaim: true,
DisableSubdomainPersistence: true,
})
app.Use(sessManager.Handler(strictSameSiteOpt))
app.Get("/login", login)
app.Get("/logout", logout)
app.Get("/secret", secret)
app.Listen(":8080")
}
func login(ctx iris.Context) {
// Authentication goes here
session := sessions.Get(ctx)
// ...
oldSessionID := session.ID()
ctx.Application().Logger().Infof("old session id = %s", oldSessionID)
// Set user as authenticated
// If AllowReclaim is false:
// manually remove the request cookie, so the new Start creates a new session:
// ctx.Request().Header.Del(sessionCookieName)
// Use session's manager instead of session.Destroy so
// it destroys the response AND request (if AllowReclaim is true)
// cookie information.
sessManager.Destroy(ctx)
session = sessManager.Start(ctx, strictSameSiteOpt)
session.Set("authenticated", true)
ctx.Application().Logger().Infof("new session id = %s", session.ID())
ctx.Redirect("/secret", iris.StatusTemporaryRedirect)
}
func logout(ctx iris.Context) {
sessManager.Destroy(ctx)
ctx.HTML(`<a href="/login">login</a>`)
}
func secret(ctx iris.Context) {
session := sessions.Get(ctx)
// Check if user is authenticated.
if auth, _ := session.GetBoolean("authenticated"); !auth {
ctx.StatusCode(iris.StatusForbidden)
ctx.HTML(`Not logged in or session invalid <a href="/logout">logout</a>`)
return
}
// Print secret message.
ctx.HTML(`The cake is a lie! <a href="/logout">logout</a>`)
} |
It's working but i found a way to reproduce (but not with an example app) it so i'm currently digging into the iris code (12.2.0-beta4) to find a clue what's happening. If the user doesn't have a session, everything is fine. When the user is redirected to oauth provider and I restart the app (using internal session storage) the login fails.
on the login page (where i start oauth and redirect to user to the provider) i then get a for whatever reason:
so the user is trapped in an endless invalid session loop. also while trying several things sometimes i encounter a
|
to fix the endless invalid session loop i've moved github.com/kataras/iris/v12/sessions/sessions.go: // Destroy removes the session data, the associated cookie
// and the Context's session value.
// Next calls of `sessions.Get` will occur to a nil Session,
// use `Sessions#Start` method for renewal
// or use the Session's Destroy method which does keep the session entry with its values cleared.
func (s *Sessions) Destroy(ctx *context.Context) {
cookieValue := s.getCookieValue(ctx, nil)
ctx.RemoveCookie(s.config.Cookie, s.cookieOptions...)
if cookieValue == "" { // nothing to destroy
return
}
ctx.Values().Remove(sessionContextKey)
//ctx.RemoveCookie(s.config.Cookie, s.cookieOptions...)
s.provider.Destroy(cookieValue)
} |
found one issue: |
It should happen if you have allow it, you should register the middleware after the |
You mean that the loop you said, is still valid? If so, could you please post an example with steps to re-produce it and test the solution by myself? |
Describe the bug
When using securecookie for session it fails sometime because plain (decoded) cookie value is passed to decode function.
securecookie decode fails with "securecookie: the value is not valid" on MAC check/decode: https://github.com/gorilla/securecookie/blob/master/securecookie.go#L323
To Reproduce
and add alter
Decode
function in securecookie:result is:
Desktop (please complete the following information):
iris.Version
v12.2.0-beta3
master
The text was updated successfully, but these errors were encountered: