Use CSP header to restrict resource loading
This helps mitigate XSS exploits. Users will have to save the settings again to make the custom CSS work.
This commit is contained in:
parent
ed521dd33d
commit
67b13c71ba
|
@ -27,6 +27,7 @@ type Settings struct {
|
||||||
AntiDopamineMode bool `json:"adm,omitempty"`
|
AntiDopamineMode bool `json:"adm,omitempty"`
|
||||||
HideUnsupportedNotifs bool `json:"hun,omitempty"`
|
HideUnsupportedNotifs bool `json:"hun,omitempty"`
|
||||||
CSS string `json:"css,omitempty"`
|
CSS string `json:"css,omitempty"`
|
||||||
|
CSSHash string `json:"cssh,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSettings() *Settings {
|
func NewSettings() *Settings {
|
||||||
|
@ -43,5 +44,6 @@ func NewSettings() *Settings {
|
||||||
AntiDopamineMode: false,
|
AntiDopamineMode: false,
|
||||||
HideUnsupportedNotifs: false,
|
HideUnsupportedNotifs: false,
|
||||||
CSS: "",
|
CSS: "",
|
||||||
|
CSSHash: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
|
@ -1014,9 +1016,19 @@ func (s *service) SaveSettings(c *client, settings *model.Settings) (err error)
|
||||||
default:
|
default:
|
||||||
return errInvalidArgument
|
return errInvalidArgument
|
||||||
}
|
}
|
||||||
|
if len(settings.CSS) > 0 {
|
||||||
if len(settings.CSS) > 1<<20 {
|
if len(settings.CSS) > 1<<20 {
|
||||||
return errInvalidArgument
|
return errInvalidArgument
|
||||||
}
|
}
|
||||||
|
// For some reason, browsers convert CRLF to LF before calculating
|
||||||
|
// the hash of the inline resources.
|
||||||
|
settings.CSS = strings.ReplaceAll(settings.CSS, "\x0d\x0a", "\x0a")
|
||||||
|
|
||||||
|
h := sha256.Sum256([]byte(settings.CSS))
|
||||||
|
settings.CSSHash = base64.StdEncoding.EncodeToString(h[:])
|
||||||
|
} else {
|
||||||
|
settings.CSSHash = ""
|
||||||
|
}
|
||||||
c.s.Settings = *settings
|
c.s.Settings = *settings
|
||||||
return c.setSession(c.s)
|
return c.setSession(c.s)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,16 @@ const (
|
||||||
CSRF
|
CSRF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const csp = "default-src 'none';" +
|
||||||
|
" img-src *;" +
|
||||||
|
" media-src *;" +
|
||||||
|
" font-src *;" +
|
||||||
|
" child-src *;" +
|
||||||
|
" connect-src 'self';" +
|
||||||
|
" form-action 'self';" +
|
||||||
|
" script-src 'self';" +
|
||||||
|
" style-src 'self'"
|
||||||
|
|
||||||
func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
|
func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
|
@ -58,14 +68,14 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
var ct string
|
h := c.w.Header()
|
||||||
switch rt {
|
switch rt {
|
||||||
case HTML:
|
case HTML:
|
||||||
ct = "text/html; charset=utf-8"
|
h.Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
h.Set("Content-Security-Policy", csp)
|
||||||
case JSON:
|
case JSON:
|
||||||
ct = "application/json"
|
h.Set("Content-Type", "application/json")
|
||||||
}
|
}
|
||||||
c.w.Header().Add("Content-Type", ct)
|
|
||||||
|
|
||||||
err = c.authenticate(at, s.instance)
|
err = c.authenticate(at, s.instance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -73,6 +83,13 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override the CSP header to allow custom CSS
|
||||||
|
if rt == HTML && len(c.s.Settings.CSS) > 0 &&
|
||||||
|
len(c.s.Settings.CSSHash) > 0 {
|
||||||
|
v := fmt.Sprintf("%s 'sha256-%s'", csp, c.s.Settings.CSSHash)
|
||||||
|
h.Set("Content-Security-Policy", v)
|
||||||
|
}
|
||||||
|
|
||||||
err = f(c)
|
err = f(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(c, err, rt, req.Method == http.MethodGet)
|
writeError(c, err, rt, req.Method == http.MethodGet)
|
||||||
|
|
Loading…
Reference in New Issue