From 3ad72fa35f8e326dbd6a82e5ba1833040ed29c6f Mon Sep 17 00:00:00 2001 From: Pawel Boguslawski Date: Fri, 2 Dec 2022 17:43:58 +0100 Subject: [PATCH 1/3] Protection against CSRF added Author-Change-Id: IB#1129006 --- go.mod | 1 + go.sum | 2 ++ modules/setting/cors.go | 14 ++++++++++++- routers/common/middleware.go | 40 +++++++++++++++++++++++++++++++++++- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index e06ccfe13d84f..2b92e64b0fade 100644 --- a/go.mod +++ b/go.mod @@ -235,6 +235,7 @@ require ( github.com/prometheus/procfs v0.7.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect + github.com/rs/cors v1.8.2 // indirect github.com/rs/xid v1.4.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect diff --git a/go.sum b/go.sum index 9c99adfbe14cc..b6b5f743056b6 100644 --- a/go.sum +++ b/go.sum @@ -1360,6 +1360,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= +github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= diff --git a/modules/setting/cors.go b/modules/setting/cors.go index a843194ff981d..71670572f42fc 100644 --- a/modules/setting/cors.go +++ b/modules/setting/cors.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -8,8 +8,13 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "github.com/rs/cors" ) +// Cors handles CORS requests and allows other middlewares +// to check whetcher request marches CORS allowed origins. +var Cors *cors.Cors + // CORSConfig defines CORS settings var CORSConfig = struct { Enabled bool @@ -33,6 +38,13 @@ func newCORSService() { } if CORSConfig.Enabled { + Cors = cors.New(cors.Options{ + AllowedOrigins: CORSConfig.AllowDomain, + AllowedMethods: CORSConfig.Methods, + AllowCredentials: CORSConfig.AllowCredentials, + MaxAge: int(CORSConfig.MaxAge.Seconds()), + }) + log.Info("CORS Service Enabled") } } diff --git a/routers/common/middleware.go b/routers/common/middleware.go index 6ea1e1dfbe5ea..7d4961ffda840 100644 --- a/routers/common/middleware.go +++ b/routers/common/middleware.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. +// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -79,5 +79,43 @@ func Middlewares() []func(http.Handler) http.Handler { next.ServeHTTP(resp, req) }) }) + + // Add CSRF handler. + handlers = append(handlers, csrfHandler()) + return handlers } + +// csfrHandler blocks recognized CSRF attempts. +// WARNING: for this proctection to work, web browser compatible with +// Fetch Metadata Request Headers (https://w3c.github.io/webappsec-fetch-metadata) +// must be used. +func csrfHandler() func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + + // Put header names we use for CSRF recognition into Vary response header. + if setting.CORSConfig.Enabled { + resp.Header().Set("Vary", "Origin, Sec-Fetch-Site") + } else { + resp.Header().Set("Vary", "Sec-Fetch-Site") + } + + // Allow requests not recognized as CSRF. + secFetchSite := strings.ToLower(req.Header.Get("Sec-Fetch-Site")) + if req.Method == "GET" || // GET must not be used for changing state (CSRF resistant). + secFetchSite == "" || // Accept requests from clients without Fetch Metadata Request Headers support. + secFetchSite == "same-origin" || // Accept requests from own origin. + secFetchSite == "none" || // Accept requests initiated by user (i.e. using bookmark). + ((secFetchSite == "same-site" || secFetchSite == "cross-site") && // Accept cross site requests allowed by CORS. + setting.CORSConfig.Enabled && setting.Cors.OriginAllowed(req)) { + next.ServeHTTP(resp, req) + return + } + + // Forbid and log other requests as CSRF. + log.Error("CSRF rejected: METHOD=\"%s\", Origin=\"%s\", Sec-Fetch-Site=\"%s\"", req.Method, req.Header.Get("Origin"), secFetchSite) + http.Error(resp, http.StatusText(http.StatusForbidden), http.StatusForbidden) + }) + } +} From 2b20046f3f1f016f553770761d12e9c773e55ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bogus=C5=82awski?= Date: Thu, 22 Dec 2022 17:18:32 +0100 Subject: [PATCH 2/3] Update cors.go --- modules/setting/cors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/setting/cors.go b/modules/setting/cors.go index f919e382968aa..e2231a13a0422 100644 --- a/modules/setting/cors.go +++ b/modules/setting/cors.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting From c5f3abef8b1279af1cb5e68b1f93bf11e265c29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bogus=C5=82awski?= Date: Thu, 22 Dec 2022 17:21:05 +0100 Subject: [PATCH 3/3] Update middleware.go According to https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#javascript-guidance-for-auto-inclusion-of-csrf-tokens-as-an-ajax-request-header GET, HEAD and OPTIONS should not be used for changing state. --- routers/common/middleware.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/routers/common/middleware.go b/routers/common/middleware.go index c0ab7ebad2b43..0f22d2181c151 100644 --- a/routers/common/middleware.go +++ b/routers/common/middleware.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package common @@ -102,7 +102,9 @@ func csrfHandler() func(next http.Handler) http.Handler { // Allow requests not recognized as CSRF. secFetchSite := strings.ToLower(req.Header.Get("Sec-Fetch-Site")) - if req.Method == "GET" || // GET must not be used for changing state (CSRF resistant). + if req.Method == "GET" || // GET, HEAD and OPTIONS must not be used for changing state (CSRF resistant). + req.Method == "HEAD" || + req.Method == "OPTIONS" || secFetchSite == "" || // Accept requests from clients without Fetch Metadata Request Headers support. secFetchSite == "same-origin" || // Accept requests from own origin. secFetchSite == "none" || // Accept requests initiated by user (i.e. using bookmark).