MFA fix, added IP blacklist, update webmail client
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package webui
|
||||
|
||||
import "net/http"
|
||||
|
||||
// contentSecurityPolicy — Bootstrap/Bootstrap Icons/Quill are all vendored locally
|
||||
// under static/vendor/ (no CDN dependency left anywhere), so this only needs 'self'.
|
||||
//
|
||||
// frame-ancestors 'self' (not 'none'): the webmail compose popup
|
||||
// (webmail_compose_widget.html) legitimately loads /webmail/mail/compose in an
|
||||
// <iframe> on the SAME origin — 'none' blocks ALL framing including that, which
|
||||
// broke the popup entirely (it's framing, just not the third-party clickjacking
|
||||
// kind this header exists to stop). 'self' still blocks any other site from
|
||||
// framing this app.
|
||||
const contentSecurityPolicy = "default-src 'self'; " +
|
||||
"script-src 'self' 'unsafe-inline'; " +
|
||||
"style-src 'self' 'unsafe-inline'; " +
|
||||
"font-src 'self'; " +
|
||||
"img-src 'self' data: https:; " +
|
||||
"frame-ancestors 'self'"
|
||||
|
||||
// SecurityHeaders sets standard hardening headers on every response — wrapped once
|
||||
// around the whole app's handler in main.go so admin and webmail routes (and login,
|
||||
// static assets, /health) all get it uniformly, rather than duplicating the wrap at
|
||||
// multiple mux-registration points.
|
||||
//
|
||||
// 'unsafe-inline' is required for both script-src and style-src: every template in
|
||||
// this codebase uses inline <script>/<style> blocks (no nonce or hash pipeline
|
||||
// exists), so a strict CSP would break every page. This still meaningfully narrows
|
||||
// the attack surface versus no CSP at all — it blocks loading script/style/fonts
|
||||
// from any origin other than this server and jsdelivr, which is what actually
|
||||
// matters against a stored-XSS-via-inbound-HTML-mail scenario (the message view
|
||||
// sanitizes HTML mail with bluemonday before rendering, but CSP is defense in depth
|
||||
// for exactly that class of bug).
|
||||
func SecurityHeaders(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")
|
||||
// SAMEORIGIN, not DENY: the compose popup frames /webmail/mail/compose in an
|
||||
// iframe on this same origin — DENY blocked that too (see
|
||||
// contentSecurityPolicy's frame-ancestors comment for the matching CSP fix).
|
||||
h.Set("X-Frame-Options", "SAMEORIGIN")
|
||||
h.Set("Referrer-Policy", "same-origin")
|
||||
h.Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
|
||||
h.Set("Content-Security-Policy", contentSecurityPolicy)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user