Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA. Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb, Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming, Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office (Collabora/OnlyOffice). Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort) sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
56 lines
1.8 KiB
Go
56 lines
1.8 KiB
Go
package middleware
|
||
|
||
import (
|
||
"net/http"
|
||
"net/url"
|
||
"strings"
|
||
)
|
||
|
||
// SecurityHeaders adds security headers that the app controls.
|
||
// HSTS is intentionally omitted — it belongs on the edge reverse proxy.
|
||
//
|
||
// frameSrc nennt zusätzliche Ursprünge, die in einem Rahmen erscheinen dürfen.
|
||
// Gebraucht für den Office-Server: Seine Bedienoberfläche läuft in einem
|
||
// <iframe>, und `default-src 'self'` deckt auch `frame-src` ab – ohne
|
||
// Ausnahme blockt der Browser sie, noch bevor eine Anfrage hinausgeht.
|
||
// Gemessen als „Framing 'http://…:9980/' violates … default-src 'self'".
|
||
//
|
||
// Bewusst **nur der eine konfigurierte Ursprung** und kein Platzhalter: Die
|
||
// Ausnahme soll so schmal sein wie der Anlass.
|
||
func SecurityHeaders(frameSrc ...string) func(http.Handler) http.Handler {
|
||
frame := "frame-src 'self'"
|
||
for _, raw := range frameSrc {
|
||
if origin := originOf(raw); origin != "" {
|
||
frame += " " + origin
|
||
}
|
||
}
|
||
csp := "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; " +
|
||
"img-src 'self' data: blob:; " + frame
|
||
|
||
return func(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("Referrer-Policy", "strict-origin-when-cross-origin")
|
||
h.Set("Content-Security-Policy", csp)
|
||
next.ServeHTTP(w, r)
|
||
})
|
||
}
|
||
}
|
||
|
||
// originOf reduziert eine Adresse auf Schema und Host. Ein Pfad in einer
|
||
// CSP-Quelle wäre nicht falsch, aber enger als gemeint – und ein Tippfehler
|
||
// darin fiele erst auf, wenn der Rahmen leer bleibt.
|
||
func originOf(raw string) string {
|
||
raw = strings.TrimSpace(raw)
|
||
if raw == "" {
|
||
return ""
|
||
}
|
||
u, err := url.Parse(raw)
|
||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||
return ""
|
||
}
|
||
return u.Scheme + "://" + u.Host
|
||
}
|