From ad504739421e85110a82c2b23ae1c32daf0b0a13 Mon Sep 17 00:00:00 2001 From: 0xFelix Date: Sat, 18 Apr 2026 18:52:47 +0200 Subject: [PATCH] middleware: add security headers to all responses Signed-off-by: 0xFelix --- pkg/app/app.go | 1 + pkg/middleware/securityheaders.go | 14 +++++++++++++ pkg/middleware/securityheaders_test.go | 27 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 pkg/middleware/securityheaders.go create mode 100644 pkg/middleware/securityheaders_test.go diff --git a/pkg/app/app.go b/pkg/app/app.go index 293e30e..5e70541 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -76,6 +76,7 @@ func New(cfg *config.Config) http.Handler { func handle(cfg *config.Config, handlers ...func(http.Handler) http.Handler) http.Handler { handlers = slices.Insert(handlers, 0, middleware.NewSetClientIP(cfg.TrustedProxyPrefixes)) + handlers = slices.Insert(handlers, 0, middleware.SecurityHeaders) if cfg.Debug { handlers = slices.Insert(handlers, 0, middleware.LogDebug) } diff --git a/pkg/middleware/securityheaders.go b/pkg/middleware/securityheaders.go new file mode 100644 index 0000000..a816241 --- /dev/null +++ b/pkg/middleware/securityheaders.go @@ -0,0 +1,14 @@ +package middleware + +import "net/http" + +func SecurityHeaders(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h := w.Header() + h.Set("X-Content-Type-Options", "nosniff") + h.Set("X-Frame-Options", "DENY") + h.Set("Content-Security-Policy", "default-src 'none'") + h.Set("Cache-Control", "no-store") + next.ServeHTTP(w, r) + }) +} diff --git a/pkg/middleware/securityheaders_test.go b/pkg/middleware/securityheaders_test.go new file mode 100644 index 0000000..b822883 --- /dev/null +++ b/pkg/middleware/securityheaders_test.go @@ -0,0 +1,27 @@ +package middleware_test + +import ( + "net/http" + "net/http/httptest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/0xfelix/hetzner-dnsapi-proxy/pkg/middleware" +) + +var _ = Describe("SecurityHeaders", func() { + It("sets security headers on the response", func() { + handler := middleware.SecurityHeaders(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + })) + rec := httptest.NewRecorder() + handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", http.NoBody)) + + Expect(rec.Code).To(Equal(http.StatusOK)) + Expect(rec.Header().Get("X-Content-Type-Options")).To(Equal("nosniff")) + Expect(rec.Header().Get("X-Frame-Options")).To(Equal("DENY")) + Expect(rec.Header().Get("Content-Security-Policy")).To(Equal("default-src 'none'")) + Expect(rec.Header().Get("Cache-Control")).To(Equal("no-store")) + }) +})