Skip to content

Commit 81816b3

Browse files
committed
Add SameSite setting for cookies
Fix go-gitea#5583 Signed-off-by: Andrew Thornton <art27@cantab.net>
1 parent 9db590f commit 81816b3

File tree

11 files changed

+92
-28
lines changed

11 files changed

+92
-28
lines changed

Diff for: custom/conf/app.example.ini

+2
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,8 @@ COOKIE_SECURE = false
794794
GC_INTERVAL_TIME = 86400
795795
; Session life time in seconds, default is 86400 (1 day)
796796
SESSION_LIFE_TIME = 86400
797+
; SameSite settings. Either "none", "lax", or "strict"
798+
SAME_SITE=strict
797799

798800
[picture]
799801
AVATAR_UPLOAD_PATH = data/avatars

Diff for: docs/content/doc/advanced/config-cheat-sheet.en-us.md

+2
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,8 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
557557
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
558558
- `GC_INTERVAL_TIME`: **86400**: GC interval in seconds.
559559
- `SESSION_LIFE_TIME`: **86400**: Session life time in seconds, default is 86400 (1 day)
560+
- `DOMAIN`: **\<empty\>**: Sets the cookie Domain
561+
- `SAME_SITE`: **strict** \[strict, lax, none\]: Set the SameSite setting for the cookie.
560562

561563
## Picture (`picture`)
562564

Diff for: modules/auth/sso/sso.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,20 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore
129129
}
130130
}
131131

132-
middleware.SetCookie(resp, "lang", user.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
132+
middleware.SetCookie(resp, "lang", user.Language,
133+
nil,
134+
setting.AppSubURL,
135+
setting.SessionConfig.Domain,
136+
setting.SessionConfig.Secure,
137+
true,
138+
middleware.SameSiteString(setting.SessionConfig.SameSite))
133139

134140
// Clear whatever CSRF has right now, force to generate a new one
135-
middleware.SetCookie(resp, setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
141+
middleware.SetCookie(resp, setting.CSRFCookieName, "",
142+
-1,
143+
setting.AppSubURL,
144+
setting.SessionConfig.Domain,
145+
setting.SessionConfig.Secure,
146+
true,
147+
middleware.SameSiteString(setting.SessionConfig.SameSite))
136148
}

Diff for: modules/context/auth.go

+13-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"code.gitea.io/gitea/models"
1010
"code.gitea.io/gitea/modules/log"
1111
"code.gitea.io/gitea/modules/setting"
12+
"code.gitea.io/gitea/modules/web/middleware"
1213
)
1314

1415
// ToggleOptions contains required or check options
@@ -41,7 +42,10 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
4142
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
4243
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
4344
if ctx.Req.URL.Path != "/user/events" {
44-
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
45+
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(),
46+
0,
47+
setting.AppSubURL,
48+
middleware.SameSiteString("lax")) // TODO: I think this is correct!
4549
}
4650
ctx.Redirect(setting.AppSubURL + "/user/settings/change_password")
4751
return
@@ -69,7 +73,10 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
6973
if options.SignInRequired {
7074
if !ctx.IsSigned {
7175
if ctx.Req.URL.Path != "/user/events" {
72-
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
76+
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(),
77+
0,
78+
setting.AppSubURL,
79+
middleware.SameSiteString("lax")) // TODO: I think this is correct!
7380
}
7481
ctx.Redirect(setting.AppSubURL + "/user/login")
7582
return
@@ -84,7 +91,10 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
8491
if !options.SignOutRequired && !ctx.IsSigned &&
8592
len(ctx.GetCookie(setting.CookieUserName)) > 0 {
8693
if ctx.Req.URL.Path != "/user/events" {
87-
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
94+
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(),
95+
0,
96+
setting.AppSubURL,
97+
middleware.SameSiteString("lax")) // TODO: I think this is correct!
8898
}
8999
ctx.Redirect(setting.AppSubURL + "/user/login")
90100
return

Diff for: modules/context/context.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,14 @@ func SignedUserName(req *http.Request) string {
524524
}
525525

526526
func getCsrfOpts() CsrfOptions {
527+
sameSite := http.SameSiteStrictMode
528+
switch strings.ToLower(setting.SessionConfig.SameSite) {
529+
case "none":
530+
sameSite = http.SameSiteNoneMode
531+
case "lax":
532+
sameSite = http.SameSiteLaxMode
533+
}
534+
527535
return CsrfOptions{
528536
Secret: setting.SecretKey,
529537
Cookie: setting.CSRFCookieName,
@@ -533,6 +541,7 @@ func getCsrfOpts() CsrfOptions {
533541
Header: "X-Csrf-Token",
534542
CookieDomain: setting.SessionConfig.Domain,
535543
CookiePath: setting.SessionConfig.CookiePath,
544+
SameSite: sameSite,
536545
}
537546
}
538547

@@ -597,7 +606,7 @@ func Contexter() func(next http.Handler) http.Handler {
597606
middleware.Domain(setting.SessionConfig.Domain),
598607
middleware.HTTPOnly(true),
599608
middleware.Secure(setting.SessionConfig.Secure),
600-
//middlewares.SameSite(opt.SameSite), FIXME: we need a samesite config
609+
middleware.SameSiteString(setting.SessionConfig.SameSite),
601610
)
602611
return
603612
}
@@ -607,7 +616,7 @@ func Contexter() func(next http.Handler) http.Handler {
607616
middleware.Domain(setting.SessionConfig.Domain),
608617
middleware.HTTPOnly(true),
609618
middleware.Secure(setting.SessionConfig.Secure),
610-
//middleware.SameSite(), FIXME: we need a samesite config
619+
middleware.SameSiteString(setting.SessionConfig.SameSite),
611620
)
612621
})
613622

Diff for: modules/setting/session.go

+4
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@ var (
3131
Secure bool
3232
// Cookie domain name. Default is empty.
3333
Domain string
34+
// SameSite declares if your cookie should be restricted to a first-party or same-site context. Valid strings are "none", "lax", "strict". Default is "strict"
35+
SameSite string
3436
}{
3537
CookieName: "i_like_gitea",
3638
Gclifetime: 86400,
3739
Maxlifetime: 86400,
40+
SameSite: "strict",
3841
}
3942
)
4043

@@ -52,6 +55,7 @@ func newSessionService() {
5255
SessionConfig.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(86400)
5356
SessionConfig.Maxlifetime = sec.Key("SESSION_LIFE_TIME").MustInt64(86400)
5457
SessionConfig.Domain = sec.Key("DOMAIN").String()
58+
SessionConfig.SameSite = sec.Key("SAME_SITE").In("strict", []string{"none", "lax", "strict"})
5559

5660
json := jsoniter.ConfigCompatibleWithStandardLibrary
5761
shadowConfig, err := json.Marshal(SessionConfig)

Diff for: modules/web/middleware/cookie.go

+15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package middleware
88
import (
99
"net/http"
1010
"net/url"
11+
"strings"
1112
"time"
1213

1314
"code.gitea.io/gitea/modules/setting"
@@ -63,6 +64,20 @@ func SameSite(sameSite http.SameSite) func(*http.Cookie) {
6364
}
6465
}
6566

67+
// SameSiteString sets the SameSite for a provided cookie
68+
func SameSiteString(sameSite string) func(*http.Cookie) {
69+
return func(c *http.Cookie) {
70+
switch strings.ToLower(sameSite) {
71+
case "none":
72+
c.SameSite = http.SameSiteNoneMode
73+
case "lax":
74+
c.SameSite = http.SameSiteLaxMode
75+
default:
76+
c.SameSite = http.SameSiteStrictMode
77+
}
78+
}
79+
}
80+
6681
// NewCookie creates a cookie
6782
func NewCookie(name, value string, maxAge int) *http.Cookie {
6883
return &http.Cookie{

Diff for: modules/web/middleware/locale.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package middleware
77
import (
88
"net/http"
99

10+
"code.gitea.io/gitea/modules/setting"
1011
"code.gitea.io/gitea/modules/translation"
1112

1213
"github.com/unknwon/i18n"
@@ -42,7 +43,13 @@ func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale {
4243
}
4344

4445
if changeLang {
45-
SetCookie(resp, "lang", lang, 1<<31-1)
46+
SetCookie(resp, "lang", lang,
47+
nil,
48+
setting.AppSubURL,
49+
setting.SessionConfig.Domain,
50+
setting.SessionConfig.Secure,
51+
true,
52+
SameSiteString(setting.SessionConfig.SameSite))
4653
}
4754

4855
return translation.NewLocale(lang)

Diff for: routers/user/auth.go

+18-17
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"code.gitea.io/gitea/modules/setting"
2525
"code.gitea.io/gitea/modules/timeutil"
2626
"code.gitea.io/gitea/modules/web"
27+
"code.gitea.io/gitea/modules/web/middleware"
2728
"code.gitea.io/gitea/routers/utils"
2829
"code.gitea.io/gitea/services/externalaccount"
2930
"code.gitea.io/gitea/services/mailer"
@@ -64,8 +65,8 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
6465
defer func() {
6566
if !isSucceed {
6667
log.Trace("auto-login cookie cleared: %s", uname)
67-
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
68-
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
68+
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
69+
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
6970
}
7071
}()
7172

@@ -95,7 +96,7 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
9596
return false, err
9697
}
9798

98-
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
99+
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
99100
return true, nil
100101
}
101102

@@ -109,13 +110,13 @@ func checkAutoLogin(ctx *context.Context) bool {
109110

110111
redirectTo := ctx.Query("redirect_to")
111112
if len(redirectTo) > 0 {
112-
ctx.SetCookie("redirect_to", redirectTo, 0, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
113+
ctx.SetCookie("redirect_to", redirectTo, 0, setting.AppSubURL, "", setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
113114
} else {
114115
redirectTo = ctx.GetCookie("redirect_to")
115116
}
116117

117118
if isSucceed {
118-
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
119+
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
119120
ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL))
120121
return true
121122
}
@@ -497,9 +498,9 @@ func handleSignIn(ctx *context.Context, u *models.User, remember bool) {
497498
func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyRedirect bool) string {
498499
if remember {
499500
days := 86400 * setting.LogInRememberDays
500-
ctx.SetCookie(setting.CookieUserName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
501+
ctx.SetCookie(setting.CookieUserName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
501502
ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
502-
setting.CookieRememberName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
503+
setting.CookieRememberName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
503504
}
504505

505506
_ = ctx.Session.Delete("openid_verified_uri")
@@ -530,10 +531,10 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
530531
}
531532
}
532533

533-
ctx.SetCookie("lang", u.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
534+
ctx.SetCookie("lang", u.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
534535

535536
// Clear whatever CSRF has right now, force to generate a new one
536-
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
537+
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
537538

538539
// Register last login
539540
u.SetLastLogin()
@@ -543,7 +544,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
543544
}
544545

545546
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) {
546-
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
547+
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
547548
if obeyRedirect {
548549
ctx.RedirectToFirst(redirectTo)
549550
}
@@ -649,7 +650,7 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
649650
}
650651

651652
// Clear whatever CSRF has right now, force to generate a new one
652-
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
653+
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
653654

654655
// Register last login
655656
u.SetLastLogin()
@@ -664,7 +665,7 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
664665
}
665666

666667
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 {
667-
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
668+
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
668669
ctx.RedirectToFirst(redirectTo)
669670
return
670671
}
@@ -1042,11 +1043,11 @@ func LinkAccountPostRegister(ctx *context.Context) {
10421043
func HandleSignOut(ctx *context.Context) {
10431044
_ = ctx.Session.Flush()
10441045
_ = ctx.Session.Destroy(ctx.Resp, ctx.Req)
1045-
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
1046-
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
1047-
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
1048-
ctx.SetCookie("lang", "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) // Setting the lang cookie will trigger the middleware to reset the language ot previous state.
1049-
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL) // logout default should set redirect to to default
1046+
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
1047+
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
1048+
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
1049+
ctx.SetCookie("lang", "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite)) // Setting the lang cookie will trigger the middleware to reset the language ot previous state.
1050+
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL) // logout default should set redirect to to default
10501051
}
10511052

10521053
// SignOut sign out from login status

Diff for: routers/user/auth_openid.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"code.gitea.io/gitea/modules/setting"
2121
"code.gitea.io/gitea/modules/timeutil"
2222
"code.gitea.io/gitea/modules/web"
23+
"code.gitea.io/gitea/modules/web/middleware"
2324
"code.gitea.io/gitea/services/mailer"
2425
)
2526

@@ -47,13 +48,13 @@ func SignInOpenID(ctx *context.Context) {
4748

4849
redirectTo := ctx.Query("redirect_to")
4950
if len(redirectTo) > 0 {
50-
ctx.SetCookie("redirect_to", redirectTo, 0, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
51+
ctx.SetCookie("redirect_to", redirectTo, 0, setting.AppSubURL, "", setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
5152
} else {
5253
redirectTo = ctx.GetCookie("redirect_to")
5354
}
5455

5556
if isSucceed {
56-
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
57+
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
5758
ctx.RedirectToFirst(redirectTo)
5859
return
5960
}

Diff for: routers/user/setting/profile.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"code.gitea.io/gitea/modules/setting"
2222
"code.gitea.io/gitea/modules/util"
2323
"code.gitea.io/gitea/modules/web"
24+
"code.gitea.io/gitea/modules/web/middleware"
2425

2526
"github.com/unknwon/i18n"
2627
)
@@ -116,7 +117,7 @@ func ProfilePost(ctx *context.Context) {
116117
}
117118

118119
// Update the language to the one we just set
119-
ctx.SetCookie("lang", ctx.User.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
120+
ctx.SetCookie("lang", ctx.User.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true, middleware.SameSiteString(setting.SessionConfig.SameSite))
120121

121122
log.Trace("User settings updated: %s", ctx.User.Name)
122123
ctx.Flash.Success(i18n.Tr(ctx.User.Language, "settings.update_profile_success"))

0 commit comments

Comments
 (0)