Commit 1c142752 by Marcus Efraimsson Committed by GitHub

Security: Add new setting allow_embedding (#16853)

When allow_embedding is false (default) the Grafana backend 
will set the http header `X-Frame-Options: deny` in all responses 
to non-static content which will instruct browser to not allow 
Grafana to be embedded in `<frame>`, `<iframe>`, 
`<embed>` or `<object>`.

Closes #14189
parent 44e6da6b
......@@ -176,6 +176,9 @@ cookie_secure = false
# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict" and "none"
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.
allow_embedding = false
#################################### Snapshots ###########################
[snapshots]
# snapshot sharing options
......
......@@ -172,6 +172,9 @@ log_queries =
# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict" and "none"
;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.
;allow_embedding = false
#################################### Snapshots ###########################
[snapshots]
# snapshot sharing options
......
......@@ -314,6 +314,12 @@ Set to `true` if you host Grafana behind HTTPS. Default is `false`.
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`.
### allow_embedding
When `false`, the HTTP header `X-Frame-Options: deny` will be set in Grafana HTTP responses which will instruct
browsers to not allow rendering Grafana in a `<frame>`, `<iframe>`, `<embed>` or `<object>`. The main goal is to
mitigate the risk of [Clickjacking](https://www.owasp.org/index.php/Clickjacking). Default is `false`.
<hr />
## [users]
......
......@@ -152,6 +152,8 @@ The default cookie name for storing the auth token is `grafana_session`. you can
## Upgrading to v6.2
### Ensure encryption of datasource secrets
Datasources store passwords and basic auth passwords in secureJsonData encrypted by default. Existing datasource
will keep working with unencrypted passwords. If you want to migrate to encrypted storage for your existing datasources
you can do that by:
......@@ -160,3 +162,9 @@ password and save the datasource.
- For datasources created by provisioning, you need to update your config file and use secureJsonData.password or
secureJsonData.basicAuthPassword field. See [provisioning docs](/administration/provisioning) for example of current
configuration.
### Embedding Grafana
If you're embedding Grafana in a `<frame>`, `<iframe>`, `<embed>` or `<object>` on a different website it will no longer work due to a new setting
that per default instructs the browser to not allow Grafana to be embedded. Read more [here](/installation/configuration/#allow-embedding) about
this new setting.
......@@ -237,6 +237,10 @@ func AddDefaultResponseHeaders() macaron.Handler {
if !strings.HasPrefix(ctx.Req.URL.Path, "/api/datasources/proxy/") {
AddNoCacheHeaders(ctx.Resp)
}
if !setting.AllowEmbedding {
AddXFrameOptionsDenyHeader(w)
}
})
}
}
......@@ -246,3 +250,7 @@ func AddNoCacheHeaders(w macaron.ResponseWriter) {
w.Header().Add("Pragma", "no-cache")
w.Header().Add("Expires", "-1")
}
func AddXFrameOptionsDenyHeader(w macaron.ResponseWriter) {
w.Header().Add("X-Frame-Options", "deny")
}
......@@ -49,7 +49,7 @@ func TestMiddlewareContext(t *testing.T) {
So(sc.resp.Header().Get("Expires"), ShouldBeEmpty)
})
middlewareScenario(t, "middleware should add Cache-Control header for GET requests with html response", func(sc *scenarioContext) {
middlewareScenario(t, "middleware should add Cache-Control header for requests with html response", func(sc *scenarioContext) {
sc.handler(func(c *m.ReqContext) {
data := &dtos.IndexViewData{
User: &dtos.CurrentUser{},
......@@ -65,6 +65,17 @@ func TestMiddlewareContext(t *testing.T) {
So(sc.resp.Header().Get("Expires"), ShouldEqual, "-1")
})
middlewareScenario(t, "middleware should add X-Frame-Options header with deny for request when not allowing embedding", func(sc *scenarioContext) {
sc.fakeReq("GET", "/api/search").exec()
So(sc.resp.Header().Get("X-Frame-Options"), ShouldEqual, "deny")
})
middlewareScenario(t, "middleware should not add X-Frame-Options header for request when allowing embedding", func(sc *scenarioContext) {
setting.AllowEmbedding = true
sc.fakeReq("GET", "/api/search").exec()
So(sc.resp.Header().Get("X-Frame-Options"), ShouldBeEmpty)
})
middlewareScenario(t, "Invalid api key", func(sc *scenarioContext) {
sc.apiKey = "invalid_key_test"
sc.fakeReq("GET", "/").exec()
......
......@@ -93,6 +93,7 @@ var (
DisableBruteForceLoginProtection bool
CookieSecure bool
CookieSameSite http.SameSite
AllowEmbedding bool
// Snapshots
ExternalSnapshotUrl string
......@@ -690,6 +691,8 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
cfg.CookieSameSite = CookieSameSite
}
AllowEmbedding = security.Key("allow_embedding").MustBool(false)
// read snapshots settings
snapshots := iniFile.Section("snapshots")
ExternalSnapshotUrl, err = valueAsString(snapshots, "external_snapshot_url", "")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment