mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 14:42:13 +08:00
Add disabled option for cookie samesite attribute (#21472)
Breaking change: If disabled the cookie samesite cookie attribute will not be set, but if none the attribute will be set and is a breaking change compared to before where none did not render the attribute. This was due to a known issue in Safari. Co-Authored-By: Arve Knudsen <arve.knudsen@gmail.com> Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> Fixes #19847
This commit is contained in:

committed by
GitHub

parent
492912845f
commit
a1579283a6
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
## Breaking changes
|
## Breaking changes
|
||||||
* **PagerDuty**: Change `payload.custom_details` field in PagerDuty notification to be a JSON object instead of a string.
|
* **PagerDuty**: Change `payload.custom_details` field in PagerDuty notification to be a JSON object instead of a string.
|
||||||
|
* **Security**: The `[security]` setting `cookie_samesite` configured to `none` now renders cookies with `SameSite=None` attribute compared to before where no `SameSite` attribute was added to cookies. To get the old behavior, use value `disabled` instead of `none`. Refer to [Upgrade Grafana](https://grafana.com/docs/grafana/latest/installation/upgrading/#upgrading-to-v6-6) for more information.
|
||||||
|
|
||||||
# 6.5.2 (2019-12-11)
|
# 6.5.2 (2019-12-11)
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ disable_brute_force_login_protection = false
|
|||||||
# set to true if you host Grafana behind HTTPS. default is false.
|
# set to true if you host Grafana behind HTTPS. default is false.
|
||||||
cookie_secure = false
|
cookie_secure = false
|
||||||
|
|
||||||
# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict" and "none"
|
# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled"
|
||||||
cookie_samesite = lax
|
cookie_samesite = lax
|
||||||
|
|
||||||
# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
|
# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
|
||||||
|
@ -180,7 +180,7 @@
|
|||||||
# set to true if you host Grafana behind HTTPS. default is false.
|
# set to true if you host Grafana behind HTTPS. default is false.
|
||||||
;cookie_secure = false
|
;cookie_secure = false
|
||||||
|
|
||||||
# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict" and "none"
|
# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled"
|
||||||
;cookie_samesite = lax
|
;cookie_samesite = lax
|
||||||
|
|
||||||
# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
|
# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
|
||||||
|
@ -361,7 +361,7 @@ Set to `true` if you host Grafana behind HTTPS. Default is `false`.
|
|||||||
|
|
||||||
### cookie_samesite
|
### cookie_samesite
|
||||||
|
|
||||||
Sets the `SameSite` cookie attribute and prevents the browser from sending this cookie along with cross-site requests. The main goal is mitigate the risk of cross-origin information leakage. It also provides some protection against cross-site request forgery attacks (CSRF), [read more here](https://www.owasp.org/index.php/SameSite). Valid values are `lax`, `strict` and `none`. Default is `lax`.
|
Sets the `SameSite` cookie attribute and prevents the browser from sending this cookie along with cross-site requests. The main goal is to mitigate the risk of cross-origin information leakage. This setting also provides some protection against cross-site request forgery attacks (CSRF), [read more about SameSite here](https://www.owasp.org/index.php/SameSite). Valid values are `lax`, `strict`, `none`, and `disabled`. Default is `lax`. Using value `disabled` does not add any `SameSite` attribute to cookies.
|
||||||
|
|
||||||
### allow_embedding
|
### allow_embedding
|
||||||
|
|
||||||
|
@ -226,3 +226,14 @@ The handling of multi-valued template variables in dimension values has been cha
|
|||||||
## Upgrading to v6.6
|
## Upgrading to v6.6
|
||||||
|
|
||||||
The Generic OAuth setting `send_client_credentials_via_post`, used for supporting non-compliant providers, has been removed. From now on, Grafana will automatically detect if credentials should be sent as part of the URL or request body for a specific provider. The result will be remembered and used for additional OAuth requests for that provider.
|
The Generic OAuth setting `send_client_credentials_via_post`, used for supporting non-compliant providers, has been removed. From now on, Grafana will automatically detect if credentials should be sent as part of the URL or request body for a specific provider. The result will be remembered and used for additional OAuth requests for that provider.
|
||||||
|
|
||||||
|
### Important changes regarding SameSite cookie attribute
|
||||||
|
|
||||||
|
Chrome 80 treats cookies as `SameSite=Lax` by default if no `SameSite` attribute is specified, see https://www.chromestatus.com/feature/5088147346030592.
|
||||||
|
|
||||||
|
Due to this change in Chrome, the `[security]` setting `cookie_samesite` configured to `none` now renders cookies with `SameSite=None` attribute compared to before where no `SameSite` attribute was added to cookies. To get the old behavior, use value `disabled` instead of `none`, see [cookie_samesite in Configuration]({{< relref "configuration/#cookie-samesite" >}}) for more information.
|
||||||
|
|
||||||
|
**Note:** There is currently a bug affecting Mac OSX and iOS that causes `SameSite=None` cookies to be treated as `SameSite=Strict` and therefore not sent with cross-site requests. (See https://bugs.webkit.org/show_bug.cgi?id=198181.) Until this is fixed, `SameSite=None` might not work properly on Safari.
|
||||||
|
|
||||||
|
This version of Chrome also rejects insecure `SameSite=None` cookies. See https://www.chromestatus.com/feature/5633521622188032 for more information. Make sure that you
|
||||||
|
change the `[security]` setting `cookie_secure` to `true` and use HTTPS when `cookie_samesite` is configured to `none`, otherwise authentication in Grafana won't work properly.
|
||||||
|
@ -32,7 +32,7 @@ https://play.grafana.org/d/000000012/grafana-play-home?orgId=1&from=156871968017
|
|||||||
|
|
||||||
You can embed a panel using an iframe on another web site. This tab will show you the html that you need to use.
|
You can embed a panel using an iframe on another web site. This tab will show you the html that you need to use.
|
||||||
|
|
||||||
> *Notice* This sharing require either anonymous access or setting [cookie_samesite]({{< relref "../installation/configuration.md#cookie-samesite" >}}) to none
|
> **Note:** This sharing requires [allow_embedding]({{< relref "../installation/configuration.md#allow-embedding" >}}) enabled and anonymous access, or proper configuration of the [cookie_samesite]({{< relref "../installation/configuration.md#cookie-samesite" >}}) setting.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -45,7 +45,8 @@ func (hs *HTTPServer) cookieOptionsFromCfg() middleware.CookieOptions {
|
|||||||
return middleware.CookieOptions{
|
return middleware.CookieOptions{
|
||||||
Path: hs.Cfg.AppSubUrl + "/",
|
Path: hs.Cfg.AppSubUrl + "/",
|
||||||
Secure: hs.Cfg.CookieSecure,
|
Secure: hs.Cfg.CookieSecure,
|
||||||
SameSite: hs.Cfg.CookieSameSite,
|
SameSiteDisabled: hs.Cfg.CookieSameSiteDisabled,
|
||||||
|
SameSiteMode: hs.Cfg.CookieSameSiteMode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
|
|||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Path: setting.AppSubUrl + "/",
|
Path: setting.AppSubUrl + "/",
|
||||||
Secure: hs.Cfg.CookieSecure,
|
Secure: hs.Cfg.CookieSecure,
|
||||||
SameSite: hs.Cfg.CookieSameSite,
|
SameSite: hs.Cfg.CookieSameSiteMode,
|
||||||
}
|
}
|
||||||
sc.m.Get(sc.url, sc.defaultHandler)
|
sc.m.Get(sc.url, sc.defaultHandler)
|
||||||
sc.fakeReqNoAssertionsWithCookie("GET", sc.url, cookie).exec()
|
sc.fakeReqNoAssertionsWithCookie("GET", sc.url, cookie).exec()
|
||||||
@ -204,7 +204,7 @@ func TestLoginViewRedirect(t *testing.T) {
|
|||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Path: hs.Cfg.AppSubUrl + "/",
|
Path: hs.Cfg.AppSubUrl + "/",
|
||||||
Secure: hs.Cfg.CookieSecure,
|
Secure: hs.Cfg.CookieSecure,
|
||||||
SameSite: hs.Cfg.CookieSameSite,
|
SameSite: hs.Cfg.CookieSameSiteMode,
|
||||||
}
|
}
|
||||||
sc.m.Get(sc.url, sc.defaultHandler)
|
sc.m.Get(sc.url, sc.defaultHandler)
|
||||||
sc.fakeReqNoAssertionsWithCookie("GET", sc.url, cookie).exec()
|
sc.fakeReqNoAssertionsWithCookie("GET", sc.url, cookie).exec()
|
||||||
@ -312,7 +312,7 @@ func TestLoginPostRedirect(t *testing.T) {
|
|||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Path: hs.Cfg.AppSubUrl + "/",
|
Path: hs.Cfg.AppSubUrl + "/",
|
||||||
Secure: hs.Cfg.CookieSecure,
|
Secure: hs.Cfg.CookieSecure,
|
||||||
SameSite: hs.Cfg.CookieSameSite,
|
SameSite: hs.Cfg.CookieSameSiteMode,
|
||||||
}
|
}
|
||||||
sc.m.Post(sc.url, sc.defaultHandler)
|
sc.m.Post(sc.url, sc.defaultHandler)
|
||||||
sc.fakeReqNoAssertionsWithCookie("POST", sc.url, cookie).exec()
|
sc.fakeReqNoAssertionsWithCookie("POST", sc.url, cookie).exec()
|
||||||
|
@ -9,14 +9,16 @@ import (
|
|||||||
type CookieOptions struct {
|
type CookieOptions struct {
|
||||||
Path string
|
Path string
|
||||||
Secure bool
|
Secure bool
|
||||||
SameSite http.SameSite
|
SameSiteDisabled bool
|
||||||
|
SameSiteMode http.SameSite
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCookieOptions() CookieOptions {
|
func newCookieOptions() CookieOptions {
|
||||||
return CookieOptions{
|
return CookieOptions{
|
||||||
Path: setting.AppSubUrl + "/",
|
Path: setting.AppSubUrl + "/",
|
||||||
Secure: setting.CookieSecure,
|
Secure: setting.CookieSecure,
|
||||||
SameSite: setting.CookieSameSite,
|
SameSiteDisabled: setting.CookieSameSiteDisabled,
|
||||||
|
SameSiteMode: setting.CookieSameSiteMode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,8 +38,8 @@ func WriteCookie(w http.ResponseWriter, name string, value string, maxAge int, g
|
|||||||
Path: options.Path,
|
Path: options.Path,
|
||||||
Secure: options.Secure,
|
Secure: options.Secure,
|
||||||
}
|
}
|
||||||
if options.SameSite != http.SameSiteDefaultMode {
|
if !options.SameSiteDisabled {
|
||||||
cookie.SameSite = options.SameSite
|
cookie.SameSite = options.SameSiteMode
|
||||||
}
|
}
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
}
|
}
|
||||||
|
@ -256,12 +256,12 @@ func TestMiddlewareContext(t *testing.T) {
|
|||||||
maxAge := (maxAgeHours + time.Hour).Seconds()
|
maxAge := (maxAgeHours + time.Hour).Seconds()
|
||||||
|
|
||||||
sameSitePolicies := []http.SameSite{
|
sameSitePolicies := []http.SameSite{
|
||||||
http.SameSiteDefaultMode,
|
http.SameSiteNoneMode,
|
||||||
http.SameSiteLaxMode,
|
http.SameSiteLaxMode,
|
||||||
http.SameSiteStrictMode,
|
http.SameSiteStrictMode,
|
||||||
}
|
}
|
||||||
for _, sameSitePolicy := range sameSitePolicies {
|
for _, sameSitePolicy := range sameSitePolicies {
|
||||||
setting.CookieSameSite = sameSitePolicy
|
setting.CookieSameSiteMode = sameSitePolicy
|
||||||
expectedCookie := &http.Cookie{
|
expectedCookie := &http.Cookie{
|
||||||
Name: setting.LoginCookieName,
|
Name: setting.LoginCookieName,
|
||||||
Value: "rotated",
|
Value: "rotated",
|
||||||
@ -269,9 +269,7 @@ func TestMiddlewareContext(t *testing.T) {
|
|||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
MaxAge: int(maxAge),
|
MaxAge: int(maxAge),
|
||||||
Secure: setting.CookieSecure,
|
Secure: setting.CookieSecure,
|
||||||
}
|
SameSite: sameSitePolicy,
|
||||||
if sameSitePolicy != http.SameSiteDefaultMode {
|
|
||||||
expectedCookie.SameSite = sameSitePolicy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.fakeReq("GET", "/").exec()
|
sc.fakeReq("GET", "/").exec()
|
||||||
@ -287,6 +285,22 @@ func TestMiddlewareContext(t *testing.T) {
|
|||||||
So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, expectedCookie.String())
|
So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, expectedCookie.String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Convey("Should not set cookie with SameSite attribute when setting.CookieSameSiteDisabled is true", func() {
|
||||||
|
setting.CookieSameSiteDisabled = true
|
||||||
|
setting.CookieSameSiteMode = http.SameSiteLaxMode
|
||||||
|
expectedCookie := &http.Cookie{
|
||||||
|
Name: setting.LoginCookieName,
|
||||||
|
Value: "rotated",
|
||||||
|
Path: setting.AppSubUrl + "/",
|
||||||
|
HttpOnly: true,
|
||||||
|
MaxAge: int(maxAge),
|
||||||
|
Secure: setting.CookieSecure,
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.fakeReq("GET", "/").exec()
|
||||||
|
So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, expectedCookie.String())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
middlewareScenario(t, "Invalid/expired auth token in cookie", func(sc *scenarioContext) {
|
middlewareScenario(t, "Invalid/expired auth token in cookie", func(sc *scenarioContext) {
|
||||||
|
@ -101,7 +101,8 @@ var (
|
|||||||
DataProxyWhiteList map[string]bool
|
DataProxyWhiteList map[string]bool
|
||||||
DisableBruteForceLoginProtection bool
|
DisableBruteForceLoginProtection bool
|
||||||
CookieSecure bool
|
CookieSecure bool
|
||||||
CookieSameSite http.SameSite
|
CookieSameSiteDisabled bool
|
||||||
|
CookieSameSiteMode http.SameSite
|
||||||
AllowEmbedding bool
|
AllowEmbedding bool
|
||||||
XSSProtectionHeader bool
|
XSSProtectionHeader bool
|
||||||
ContentTypeProtectionHeader bool
|
ContentTypeProtectionHeader bool
|
||||||
@ -248,7 +249,8 @@ type Cfg struct {
|
|||||||
DisableInitAdminCreation bool
|
DisableInitAdminCreation bool
|
||||||
DisableBruteForceLoginProtection bool
|
DisableBruteForceLoginProtection bool
|
||||||
CookieSecure bool
|
CookieSecure bool
|
||||||
CookieSameSite http.SameSite
|
CookieSameSiteDisabled bool
|
||||||
|
CookieSameSiteMode http.SameSite
|
||||||
|
|
||||||
TempDataLifetime time.Duration
|
TempDataLifetime time.Duration
|
||||||
MetricsEndpointEnabled bool
|
MetricsEndpointEnabled bool
|
||||||
@ -719,18 +721,24 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if samesiteString == "disabled" {
|
||||||
|
CookieSameSiteDisabled = true
|
||||||
|
cfg.CookieSameSiteDisabled = CookieSameSiteDisabled
|
||||||
|
} else {
|
||||||
validSameSiteValues := map[string]http.SameSite{
|
validSameSiteValues := map[string]http.SameSite{
|
||||||
"lax": http.SameSiteLaxMode,
|
"lax": http.SameSiteLaxMode,
|
||||||
"strict": http.SameSiteStrictMode,
|
"strict": http.SameSiteStrictMode,
|
||||||
"none": http.SameSiteDefaultMode,
|
"none": http.SameSiteNoneMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
if samesite, ok := validSameSiteValues[samesiteString]; ok {
|
if samesite, ok := validSameSiteValues[samesiteString]; ok {
|
||||||
CookieSameSite = samesite
|
CookieSameSiteMode = samesite
|
||||||
cfg.CookieSameSite = CookieSameSite
|
cfg.CookieSameSiteMode = CookieSameSiteMode
|
||||||
} else {
|
} else {
|
||||||
CookieSameSite = http.SameSiteLaxMode
|
CookieSameSiteMode = http.SameSiteLaxMode
|
||||||
cfg.CookieSameSite = CookieSameSite
|
cfg.CookieSameSiteMode = CookieSameSiteMode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AllowEmbedding = security.Key("allow_embedding").MustBool(false)
|
AllowEmbedding = security.Key("allow_embedding").MustBool(false)
|
||||||
|
Reference in New Issue
Block a user