From 1ad694ce2b8188d08e7a3b22db6785b11a7c8a21 Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Thu, 12 Dec 2024 21:37:23 +0100 Subject: [PATCH] feat(security): #314 include all proposed security headers --- handler/middleware/cache_control.go | 3 -- .../middleware/content_security_policiy.go | 29 ------------ handler/middleware/coop.go | 13 ------ handler/middleware/corp.go | 13 ------ handler/middleware/cors.go | 23 ---------- handler/middleware/security_headers.go | 46 +++++++++++++++++++ handler/render.go | 1 + main.go | 5 +- 8 files changed, 48 insertions(+), 85 deletions(-) delete mode 100644 handler/middleware/content_security_policiy.go delete mode 100644 handler/middleware/coop.go delete mode 100644 handler/middleware/corp.go delete mode 100644 handler/middleware/cors.go create mode 100644 handler/middleware/security_headers.go diff --git a/handler/middleware/cache_control.go b/handler/middleware/cache_control.go index cc7decb..8f0f160 100644 --- a/handler/middleware/cache_control.go +++ b/handler/middleware/cache_control.go @@ -3,14 +3,11 @@ package middleware import ( "net/http" "strings" - - "me-fit/log" ) func CacheControl(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path - log.Info("path: %v", path) cached := false if strings.HasPrefix(path, "/static") { diff --git a/handler/middleware/content_security_policiy.go b/handler/middleware/content_security_policiy.go deleted file mode 100644 index aa84db9..0000000 --- a/handler/middleware/content_security_policiy.go +++ /dev/null @@ -1,29 +0,0 @@ -package middleware - -import "net/http" - -func ContentSecurityPolicy(next http.Handler) http.Handler { - - values := map[string]string{ - "default-src": "'none'", - "script-src": "'self' https://umami.me-fit.eu", - "connect-src": "'self' https://umami.me-fit.eu", - "img-src": "'self'", - "style-src": "'self'", - "form-action": "'self'", - "frame-ancestors": "'none'", - } - - var headerValue string - for key, value := range values { - headerValue += key + " " + value + "; " - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // While this value can be overridden, it can't be moved to after the next.ServeHTTP call, - // because if the response writer get's closed, the headers can't be set anymore - w.Header().Set("Content-Security-Policy", headerValue) - - next.ServeHTTP(w, r) - }) -} diff --git a/handler/middleware/coop.go b/handler/middleware/coop.go deleted file mode 100644 index 483c540..0000000 --- a/handler/middleware/coop.go +++ /dev/null @@ -1,13 +0,0 @@ -package middleware - -import ( - "net/http" -) - -func Coop(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Cross-Origin-Opener-Policy", "same-origin") - - next.ServeHTTP(w, r) - }) -} diff --git a/handler/middleware/corp.go b/handler/middleware/corp.go deleted file mode 100644 index 5f1223e..0000000 --- a/handler/middleware/corp.go +++ /dev/null @@ -1,13 +0,0 @@ -package middleware - -import ( - "net/http" -) - -func Corp(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Cross-Origin-Resource-Policy", "same-origin") - - next.ServeHTTP(w, r) - }) -} diff --git a/handler/middleware/cors.go b/handler/middleware/cors.go deleted file mode 100644 index 1d57885..0000000 --- a/handler/middleware/cors.go +++ /dev/null @@ -1,23 +0,0 @@ -package middleware - -import ( - "me-fit/types" - - "net/http" -) - -func Cors(serverSettings *types.Settings) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", serverSettings.BaseUrl) - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE") - - if r.Method == "OPTIONS" { - w.WriteHeader(http.StatusOK) - return - } - - next.ServeHTTP(w, r) - }) - } -} diff --git a/handler/middleware/security_headers.go b/handler/middleware/security_headers.go new file mode 100644 index 0000000..a6a1d08 --- /dev/null +++ b/handler/middleware/security_headers.go @@ -0,0 +1,46 @@ +package middleware + +import ( + "net/http" + + "me-fit/types" +) + +func SecurityHeaders(serverSettings *types.Settings) func(http.Handler) http.Handler { + cspValues := map[string]string{ + "default-src": "'none'", + "script-src": "'self' https://umami.me-fit.eu", + "connect-src": "'self' https://umami.me-fit.eu", + "img-src": "'self'", + "style-src": "'self'", + "form-action": "'self'", + "frame-ancestors": "'none'", + } + + var csp string + for key, value := range cspValues { + csp += key + " " + value + "; " + } + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Access-Control-Allow-Origin", serverSettings.BaseUrl) + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE") + w.Header().Set("Content-Security-Policy", csp) + w.Header().Set("Cross-Origin-Resource-Policy", "same-origin") + w.Header().Set("Cross-Origin-Opener-Policy", "same-origin") + w.Header().Set("Cross-Origin-Embedder-Policy", "require-corp") + w.Header().Set("Permissions-Policy", "geolocation=(), camera=(), microphone=()") + w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") + w.Header().Set("Permissions-Policy", "interest-cohort=()") + + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return + } + + next.ServeHTTP(w, r) + }) + } +} diff --git a/handler/render.go b/handler/render.go index 7ae9509..e2bf3e8 100644 --- a/handler/render.go +++ b/handler/render.go @@ -23,6 +23,7 @@ func NewRender(settings *types.Settings) *Render { } func (render *Render) Render(r *http.Request, w http.ResponseWriter, comp templ.Component) { + w.Header().Set("Content-Type", "text/html") err := comp.Render(r.Context(), w) if err != nil { log.Error("Failed to render layout: %v", err) diff --git a/main.go b/main.go index 3d221d9..e2d8a90 100644 --- a/main.go +++ b/main.go @@ -129,11 +129,8 @@ func createHandler(d *sql.DB, serverSettings *types.Settings) http.Handler { router, middleware.Log, middleware.CacheControl, - middleware.ContentSecurityPolicy, - middleware.Cors(serverSettings), + middleware.SecurityHeaders(serverSettings), middleware.Authenticate(authService), middleware.CrossSiteRequestForgery(authService), - middleware.Corp, - middleware.Coop, ) }