feat(security): HSTS og en lukket Permissions-Policy på hvert svar
To headere, appen ikke satte, og som begge følger af, hvad den allerede er. Sessionen bor i en __Host-cookie, der kun findes over HTTPS — hele modellen forudsætter TLS. Uden HSTS holder den forudsætning ikke mod en netværksangriber: den allerførste navigation kan strippes til http://, og cookien rækkes over i klartekst, før Secure-flaget nogensinde kommer i spil. HSTS lukker vinduet, og den er sat på nøjagtig samme betingelse som cookiens Secure-flag (secureCookies), så en dev-instans på ren HTTP aldrig bliver bedt om et certifikat, den ikke har. To år, underdomæner med; ingen preload — at blive hard-kodet ind i browsere er en beslutning for domænets ejer, ikke for en selv-hostet app. verdande beder aldrig om kamera, mikrofon eller placering, så den sikreste Permissions-Policy er at slå dem alle fra: et indsat element kan ikke bede om det, siden selv har afvist. Navngivet felt for felt frem for med et jokertegn, fordi jokertegns-formen ikke forstås ens af alle browsere, og en regel, en browser ignorerer, er ingen regel. Prøve på begge: HSTS følger HTTPS og udebliver over HTTP, og Permissions-Policy lukker for kameraet uanset skema. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3e52eea2da
commit
8a87cfeb44
@@ -3,6 +3,8 @@ package httpapi
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kristianwind/verdande/internal/config"
|
||||
)
|
||||
|
||||
// Politikken skal stå i svaret, og den skal stadig være stram.
|
||||
@@ -50,6 +52,35 @@ func TestEveryResponseCarriesAStrictCSP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// HSTS følger med, når instansen kører over HTTPS — og kun der.
|
||||
//
|
||||
// Sessionen bor i en __Host-cookie, der kun findes over TLS, så hele modellen
|
||||
// forudsætter allerede HTTPS. HSTS er det, der holder den forudsætning mod en
|
||||
// netværksangriber; men bedt om over ren HTTP ville den bede en browser om at
|
||||
// kræve et certifikat, en dev-instans ikke har. Begge halvdele skal stå prøven.
|
||||
func TestHSTSTracksHTTPS(t *testing.T) {
|
||||
overHTTP := newTestServer(t) // BaseURL er http://localhost
|
||||
resp, _ := overHTTP.do(t, "GET", "/api/v1/ping", nil)
|
||||
if got := resp.Header.Get("Strict-Transport-Security"); got != "" {
|
||||
t.Errorf("HSTS sat over ren HTTP: %q", got)
|
||||
}
|
||||
|
||||
overHTTPS := newTestServerWith(t, func(c *config.Config) {
|
||||
c.BaseURL = "https://todo.example.dk"
|
||||
})
|
||||
resp, _ = overHTTPS.do(t, "GET", "/api/v1/ping", nil)
|
||||
if got := resp.Header.Get("Strict-Transport-Security"); got == "" {
|
||||
t.Error("ingen HSTS over HTTPS")
|
||||
} else if !strings.Contains(got, "includeSubDomains") {
|
||||
t.Errorf("HSTS uden includeSubDomains: %q", got)
|
||||
}
|
||||
// Permissions-Policy står på hvert svar, uanset skema: den lukker for kamera,
|
||||
// mikrofon og placering, som appen aldrig beder om.
|
||||
if got := resp.Header.Get("Permissions-Policy"); !strings.Contains(got, "camera=()") {
|
||||
t.Errorf("Permissions-Policy lukker ikke for kameraet: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Et bilag må ikke kunne blive læst som et dokument.
|
||||
func TestAttachmentsAreNotServedAsDocuments(t *testing.T) {
|
||||
for _, mime := range []string{"image/svg+xml", "text/html", "application/xhtml+xml", "text/xml"} {
|
||||
|
||||
Reference in New Issue
Block a user