feat(web): Sessions, Rate-Limiting, Security-Header, Templates und eingebettete Assets
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NBHF4R9EAejDJUMdwr6C68
This commit is contained in:
parent
e49882b8a8
commit
bb2bc223b9
16 changed files with 1292 additions and 0 deletions
46
internal/web/security.go
Normal file
46
internal/web/security.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// contentSecurityPolicy ist so restriktiv möglich, weil sämtliche Assets
|
||||
// eingebettet und gleich-origin ausgeliefert werden. Es gibt keine externen
|
||||
// Ressourcen und kein Inline-Script.
|
||||
const contentSecurityPolicy = "default-src 'self'; " +
|
||||
"script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; " +
|
||||
"connect-src 'none'; object-src 'none'; base-uri 'none'; " +
|
||||
"form-action 'self'; frame-ancestors 'none'"
|
||||
|
||||
// SecurityHeaders setzt die Sicherheits-Header auf jede Antwort.
|
||||
func SecurityHeaders(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
h := w.Header()
|
||||
h.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
||||
h.Set("X-Content-Type-Options", "nosniff")
|
||||
h.Set("Referrer-Policy", "no-referrer")
|
||||
h.Set("X-Frame-Options", "DENY")
|
||||
h.Set("Content-Security-Policy", contentSecurityPolicy)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// NoStore verhindert jegliches Zwischenspeichern.
|
||||
func NoStore(w http.ResponseWriter) {
|
||||
w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, private")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
w.Header().Set("Expires", "0")
|
||||
}
|
||||
|
||||
// ClientIP liefert die Quell-IP der Verbindung. X-Forwarded-For wird bewusst
|
||||
// ignoriert: das Portal terminiert TLS selbst, ein gesetzter Header wäre
|
||||
// fälschbar und würde das Audit-Log entwerten.
|
||||
func ClientIP(r *http.Request) string {
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return strings.TrimSpace(r.RemoteAddr)
|
||||
}
|
||||
return host
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue