mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Add SameSite setting for cookies (#14900)
Add SameSite setting for cookies and rationalise the cookie setting code. Switches SameSite to Lax by default. There is a possible future extension of differentiating which cookies could be set at Strict by default but that is for a future PR. Fix #5583 Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
)
|
||||
|
||||
// ToggleOptions contains required or check options
|
||||
@@ -41,7 +42,7 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
||||
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
|
||||
if ctx.Req.URL.Path != "/user/events" {
|
||||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
|
||||
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
|
||||
}
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/change_password")
|
||||
return
|
||||
@@ -69,7 +70,7 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
|
||||
if options.SignInRequired {
|
||||
if !ctx.IsSigned {
|
||||
if ctx.Req.URL.Path != "/user/events" {
|
||||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
|
||||
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
|
||||
}
|
||||
ctx.Redirect(setting.AppSubURL + "/user/login")
|
||||
return
|
||||
@@ -84,7 +85,7 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
|
||||
if !options.SignOutRequired && !ctx.IsSigned &&
|
||||
len(ctx.GetCookie(setting.CookieUserName)) > 0 {
|
||||
if ctx.Req.URL.Path != "/user/events" {
|
||||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
|
||||
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
|
||||
}
|
||||
ctx.Redirect(setting.AppSubURL + "/user/login")
|
||||
return
|
||||
|
@@ -386,9 +386,28 @@ func (ctx *Context) Redirect(location string, status ...int) {
|
||||
http.Redirect(ctx.Resp, ctx.Req, location, code)
|
||||
}
|
||||
|
||||
// SetCookie set cookies to web browser
|
||||
func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
|
||||
middleware.SetCookie(ctx.Resp, name, value, others...)
|
||||
// SetCookie convenience function to set most cookies consistently
|
||||
// CSRF and a few others are the exception here
|
||||
func (ctx *Context) SetCookie(name, value string, expiry int) {
|
||||
middleware.SetCookie(ctx.Resp, name, value,
|
||||
expiry,
|
||||
setting.AppSubURL,
|
||||
setting.SessionConfig.Domain,
|
||||
setting.SessionConfig.Secure,
|
||||
true,
|
||||
middleware.SameSite(setting.SessionConfig.SameSite))
|
||||
}
|
||||
|
||||
// DeleteCookie convenience function to delete most cookies consistently
|
||||
// CSRF and a few others are the exception here
|
||||
func (ctx *Context) DeleteCookie(name string) {
|
||||
middleware.SetCookie(ctx.Resp, name, "",
|
||||
-1,
|
||||
setting.AppSubURL,
|
||||
setting.SessionConfig.Domain,
|
||||
setting.SessionConfig.Secure,
|
||||
true,
|
||||
middleware.SameSite(setting.SessionConfig.SameSite))
|
||||
}
|
||||
|
||||
// GetCookie returns given cookie value from request header.
|
||||
@@ -399,6 +418,11 @@ func (ctx *Context) GetCookie(name string) string {
|
||||
// GetSuperSecureCookie returns given cookie value from request header with secret string.
|
||||
func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
|
||||
val := ctx.GetCookie(name)
|
||||
return ctx.CookieDecrypt(secret, val)
|
||||
}
|
||||
|
||||
// CookieDecrypt returns given value from with secret string.
|
||||
func (ctx *Context) CookieDecrypt(secret, val string) (string, bool) {
|
||||
if val == "" {
|
||||
return "", false
|
||||
}
|
||||
@@ -414,14 +438,21 @@ func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
|
||||
}
|
||||
|
||||
// SetSuperSecureCookie sets given cookie value to response header with secret string.
|
||||
func (ctx *Context) SetSuperSecureCookie(secret, name, value string, others ...interface{}) {
|
||||
func (ctx *Context) SetSuperSecureCookie(secret, name, value string, expiry int) {
|
||||
text := ctx.CookieEncrypt(secret, value)
|
||||
|
||||
ctx.SetCookie(name, text, expiry)
|
||||
}
|
||||
|
||||
// CookieEncrypt encrypts a given value using the provided secret
|
||||
func (ctx *Context) CookieEncrypt(secret, value string) string {
|
||||
key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
|
||||
text, err := com.AESGCMEncrypt(key, []byte(value))
|
||||
if err != nil {
|
||||
panic("error encrypting cookie: " + err.Error())
|
||||
}
|
||||
|
||||
ctx.SetCookie(name, hex.EncodeToString(text), others...)
|
||||
return hex.EncodeToString(text)
|
||||
}
|
||||
|
||||
// GetCookieInt returns cookie result in int type.
|
||||
@@ -533,6 +564,7 @@ func getCsrfOpts() CsrfOptions {
|
||||
Header: "X-Csrf-Token",
|
||||
CookieDomain: setting.SessionConfig.Domain,
|
||||
CookiePath: setting.SessionConfig.CookiePath,
|
||||
SameSite: setting.SessionConfig.SameSite,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,17 +629,17 @@ func Contexter() func(next http.Handler) http.Handler {
|
||||
middleware.Domain(setting.SessionConfig.Domain),
|
||||
middleware.HTTPOnly(true),
|
||||
middleware.Secure(setting.SessionConfig.Secure),
|
||||
//middlewares.SameSite(opt.SameSite), FIXME: we need a samesite config
|
||||
middleware.SameSite(setting.SessionConfig.SameSite),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetCookie("macaron_flash", "", -1,
|
||||
middleware.SetCookie(ctx.Resp, "macaron_flash", "", -1,
|
||||
setting.SessionConfig.CookiePath,
|
||||
middleware.Domain(setting.SessionConfig.Domain),
|
||||
middleware.HTTPOnly(true),
|
||||
middleware.Secure(setting.SessionConfig.Secure),
|
||||
//middleware.SameSite(), FIXME: we need a samesite config
|
||||
middleware.SameSite(setting.SessionConfig.SameSite),
|
||||
)
|
||||
})
|
||||
|
||||
|
@@ -22,6 +22,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
)
|
||||
|
||||
@@ -37,6 +39,8 @@ type CSRF interface {
|
||||
GetCookiePath() string
|
||||
// Return the flag value used for the csrf token.
|
||||
GetCookieHTTPOnly() bool
|
||||
// Return cookie domain
|
||||
GetCookieDomain() string
|
||||
// Return the token.
|
||||
GetToken() string
|
||||
// Validate by token.
|
||||
@@ -93,6 +97,11 @@ func (c *csrf) GetCookieHTTPOnly() bool {
|
||||
return c.CookieHTTPOnly
|
||||
}
|
||||
|
||||
// GetCookieDomain returns the flag value used for the csrf token.
|
||||
func (c *csrf) GetCookieDomain() string {
|
||||
return c.CookieDomain
|
||||
}
|
||||
|
||||
// GetToken returns the current token. This is typically used
|
||||
// to populate a hidden form in an HTML template.
|
||||
func (c *csrf) GetToken() string {
|
||||
@@ -227,10 +236,14 @@ func Csrfer(opt CsrfOptions, ctx *Context) CSRF {
|
||||
if opt.CookieLifeTime == 0 {
|
||||
expires = time.Now().AddDate(0, 0, 1)
|
||||
}
|
||||
ctx.SetCookie(opt.Cookie, x.Token, opt.CookieLifeTime, opt.CookiePath, opt.CookieDomain, opt.Secure, opt.CookieHTTPOnly, expires,
|
||||
func(c *http.Cookie) {
|
||||
c.SameSite = opt.SameSite
|
||||
},
|
||||
middleware.SetCookie(ctx.Resp, opt.Cookie, x.Token,
|
||||
opt.CookieLifeTime,
|
||||
opt.CookiePath,
|
||||
opt.CookieDomain,
|
||||
opt.Secure,
|
||||
opt.CookieHTTPOnly,
|
||||
expires,
|
||||
middleware.SameSite(opt.SameSite),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -248,14 +261,22 @@ func Csrfer(opt CsrfOptions, ctx *Context) CSRF {
|
||||
func Validate(ctx *Context, x CSRF) {
|
||||
if token := ctx.Req.Header.Get(x.GetHeaderName()); len(token) > 0 {
|
||||
if !x.ValidToken(token) {
|
||||
ctx.SetCookie(x.GetCookieName(), "", -1, x.GetCookiePath())
|
||||
// Delete the cookie
|
||||
middleware.SetCookie(ctx.Resp, x.GetCookieName(), "",
|
||||
-1,
|
||||
x.GetCookiePath(),
|
||||
x.GetCookieDomain()) // FIXME: Do we need to set the Secure, httpOnly and SameSite values too?
|
||||
x.Error(ctx.Resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
if token := ctx.Req.FormValue(x.GetFormName()); len(token) > 0 {
|
||||
if !x.ValidToken(token) {
|
||||
ctx.SetCookie(x.GetCookieName(), "", -1, x.GetCookiePath())
|
||||
// Delete the cookie
|
||||
middleware.SetCookie(ctx.Resp, x.GetCookieName(), "",
|
||||
-1,
|
||||
x.GetCookiePath(),
|
||||
x.GetCookieDomain()) // FIXME: Do we need to set the Secure, httpOnly and SameSite values too?
|
||||
x.Error(ctx.Resp)
|
||||
}
|
||||
return
|
||||
|
Reference in New Issue
Block a user