328-invalidate-sessions #334
@@ -2,11 +2,10 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"me-fit/service"
|
"me-fit/service"
|
||||||
|
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type csrfResponseWriter struct {
|
type csrfResponseWriter struct {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"me-fit/log"
|
|
||||||
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"me-fit/log"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,33 +7,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func SecurityHeaders(serverSettings *types.Settings) func(http.Handler) http.Handler {
|
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 func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
w.Header().Set("Access-Control-Allow-Origin", serverSettings.BaseUrl)
|
w.Header().Set("Access-Control-Allow-Origin", serverSettings.BaseUrl)
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE")
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE")
|
||||||
w.Header().Set("Content-Security-Policy", csp)
|
w.Header().Set("Content-Security-Policy",
|
||||||
|
"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'",
|
||||||
|
)
|
||||||
w.Header().Set("Cross-Origin-Resource-Policy", "same-origin")
|
w.Header().Set("Cross-Origin-Resource-Policy", "same-origin")
|
||||||
w.Header().Set("Cross-Origin-Opener-Policy", "same-origin")
|
w.Header().Set("Cross-Origin-Opener-Policy", "same-origin")
|
||||||
w.Header().Set("Cross-Origin-Embedder-Policy", "require-corp")
|
w.Header().Set("Cross-Origin-Embedder-Policy", "require-corp")
|
||||||
w.Header().Set("Permissions-Policy", "geolocation=(), camera=(), microphone=()")
|
w.Header().Set("Permissions-Policy", "geolocation=(), camera=(), microphone=(), interest-cohort=()")
|
||||||
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||||||
w.Header().Set("Permissions-Policy", "interest-cohort=()")
|
|
||||||
w.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains; preload")
|
w.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains; preload")
|
||||||
|
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
|
|||||||
77
main_test.go
77
main_test.go
@@ -30,8 +30,83 @@ var (
|
|||||||
port atomic.Int32
|
port atomic.Int32
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSecurity(t *testing.T) {
|
func TestSecurityHeader(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
t.Run("should keep caching for static content", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
_, basePath, ctx := setupIntegrationTest(t)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", basePath+"/static/favicon.svg", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
cacheControl := resp.Header.Get("Cache-Control")
|
||||||
|
assert.Equal(t, "", cacheControl)
|
||||||
|
})
|
||||||
|
t.Run("should disable caching for dynamic content", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
_, basePath, ctx := setupIntegrationTest(t)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", basePath, nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
cacheControl := resp.Header.Get("Cache-Control")
|
||||||
|
assert.Equal(t, "no-cache, no-store, must-revalidate", cacheControl)
|
||||||
|
})
|
||||||
|
t.Run("should include security headers", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
_, basePath, ctx := setupIntegrationTest(t)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", basePath, nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
value := resp.Header.Get("X-Content-Type-Options")
|
||||||
|
assert.Equal(t, "nosniff", value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Access-Control-Allow-Origin")
|
||||||
|
assert.Equal(t, basePath, value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Access-Control-Allow-Methods")
|
||||||
|
assert.Equal(t, "GET, POST, DELETE", value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Content-Security-Policy")
|
||||||
|
assert.Equal(t, "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'", value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Cross-Origin-Resource-Policy")
|
||||||
|
assert.Equal(t, "same-origin", value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Cross-Origin-Opener-Policy")
|
||||||
|
assert.Equal(t, "same-origin", value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Cross-Origin-Embedder-Policy")
|
||||||
|
assert.Equal(t, "require-corp", value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Permissions-Policy")
|
||||||
|
assert.Equal(t, "geolocation=(), camera=(), microphone=(), interest-cohort=()", value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Referrer-Policy")
|
||||||
|
assert.Equal(t, "strict-origin-when-cross-origin", value)
|
||||||
|
|
||||||
|
value = resp.Header.Get("Strict-Transport-Security")
|
||||||
|
assert.Equal(t, "max-age=63072000; includeSubDomains; preload", value)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuth(t *testing.T) {
|
func TestAuth(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user