diff --git a/middleware/secure.go b/middleware/secure.go index eded54f1..77a1487f 100644 --- a/middleware/secure.go +++ b/middleware/secure.go @@ -60,6 +60,12 @@ type ( // have occurred instead of blocking the resource. // Optional. Default value false. CSPReportOnly bool `yaml:"csp_report_only"` + + // HSTSPreloadEnabled will add the preload tag in the `Strict Transport Security` + // header, which enables the domain to be included in the HSTS preload list + // maintained by Chrome (and used by Firefox and Safari): https://hstspreload.org/ + // Optional. Default value false. + HSTSPreloadEnabled bool `yaml:"hsts_preload_enabled"` } ) @@ -70,6 +76,7 @@ var ( XSSProtection: "1; mode=block", ContentTypeNosniff: "nosniff", XFrameOptions: "SAMEORIGIN", + HSTSPreloadEnabled: false, } ) @@ -112,6 +119,9 @@ func SecureWithConfig(config SecureConfig) echo.MiddlewareFunc { if !config.HSTSExcludeSubdomains { subdomains = "; includeSubdomains" } + if config.HSTSPreloadEnabled { + subdomains = fmt.Sprintf("%s; preload", subdomains) + } res.Header().Set(echo.HeaderStrictTransportSecurity, fmt.Sprintf("max-age=%d%s", config.HSTSMaxAge, subdomains)) } if config.ContentSecurityPolicy != "" { diff --git a/middleware/secure_test.go b/middleware/secure_test.go index 47121fb3..96245fea 100644 --- a/middleware/secure_test.go +++ b/middleware/secure_test.go @@ -62,4 +62,25 @@ func TestSecure(t *testing.T) { assert.Equal(t, "max-age=3600; includeSubdomains", rec.Header().Get(echo.HeaderStrictTransportSecurity)) assert.Equal(t, "default-src 'self'", rec.Header().Get(echo.HeaderContentSecurityPolicyReportOnly)) assert.Equal(t, "", rec.Header().Get(echo.HeaderContentSecurityPolicy)) + + // Custom, with preload option enabled + req.Header.Set(echo.HeaderXForwardedProto, "https") + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + SecureWithConfig(SecureConfig{ + HSTSMaxAge: 3600, + HSTSPreloadEnabled: true, + })(h)(c) + assert.Equal(t, "max-age=3600; includeSubdomains; preload", rec.Header().Get(echo.HeaderStrictTransportSecurity)) + + // Custom, with preload option enabled and subdomains excluded + req.Header.Set(echo.HeaderXForwardedProto, "https") + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + SecureWithConfig(SecureConfig{ + HSTSMaxAge: 3600, + HSTSPreloadEnabled: true, + HSTSExcludeSubdomains: true, + })(h)(c) + assert.Equal(t, "max-age=3600; preload", rec.Header().Get(echo.HeaderStrictTransportSecurity)) }