aboutsummaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorr <r@freesoftwareextremist.com>2023-10-15 15:53:44 +0000
committerr <r@freesoftwareextremist.com>2023-10-15 15:53:44 +0000
commit67b13c71baea56eeb15532ca1b1377f6da8d18ac (patch)
treec10bc1f71e283b431076fd376acf170906fa0188 /service
parented521dd33d0d002c577a75e349136fed25b7fda5 (diff)
downloadbloat-67b13c71baea56eeb15532ca1b1377f6da8d18ac.tar.gz
bloat-67b13c71baea56eeb15532ca1b1377f6da8d18ac.zip
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.
Diffstat (limited to 'service')
-rw-r--r--service/service.go16
-rw-r--r--service/transport.go25
2 files changed, 35 insertions, 6 deletions
diff --git a/service/service.go b/service/service.go
index 2f87fa3..c925b83 100644
--- a/service/service.go
+++ b/service/service.go
@@ -1,6 +1,8 @@
package service
import (
+ "crypto/sha256"
+ "encoding/base64"
"errors"
"fmt"
"mime/multipart"
@@ -1014,8 +1016,18 @@ func (s *service) SaveSettings(c *client, settings *model.Settings) (err error)
default:
return errInvalidArgument
}
- if len(settings.CSS) > 1<<20 {
- return errInvalidArgument
+ if len(settings.CSS) > 0 {
+ if len(settings.CSS) > 1<<20 {
+ 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
return c.setSession(c.s)
diff --git a/service/transport.go b/service/transport.go
index 1182d6c..d032cce 100644
--- a/service/transport.go
+++ b/service/transport.go
@@ -26,6 +26,16 @@ const (
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 {
r := mux.NewRouter()
@@ -58,14 +68,14 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
}(time.Now())
}
- var ct string
+ h := c.w.Header()
switch rt {
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:
- ct = "application/json"
+ h.Set("Content-Type", "application/json")
}
- c.w.Header().Add("Content-Type", ct)
err = c.authenticate(at, s.instance)
if err != nil {
@@ -73,6 +83,13 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
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)
if err != nil {
writeError(c, err, rt, req.Method == http.MethodGet)