Repository navigation
Expand file tree
/
Copy pathheaders.go
More file actions
63 lines (56 loc) · 1.92 KB
/
Copy pathheaders.go
File metadata and controls
63 lines (56 loc) · 1.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package main
import (
"crypto/sha256"
"encoding/base64"
"net/http"
"regexp"
"strings"
)
// Security headers.
//
// Every response says it must not be framed (X-Frame-Options plus CSP
// frame-ancestors) and must not be MIME-sniffed. The read-only UI is meant to be
// shared as a link, not embedded in someone else's page, so nothing here needs
// framing.
//
// HTML pages get a full Content-Security-Policy on top. Their inline <script>
// blocks are allowed by SHA-256 hash, computed from the embedded file itself, so
// editing a page never leaves a stale hash behind. Inline styles stay allowed:
// the pages use style="" attributes and ECharts writes inline styles.
const frameOnlyCSP = "frame-ancestors 'none'"
func securityHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h := w.Header()
h.Set("X-Frame-Options", "DENY")
h.Set("X-Content-Type-Options", "nosniff")
h.Set("Referrer-Policy", "same-origin")
h.Set("Content-Security-Policy", frameOnlyCSP)
next.ServeHTTP(w, r)
})
}
// inlineScriptRe matches attribute-less <script> blocks: the only inline form the
// pages use. TestPageScriptsAllCovered keeps it that way.
var inlineScriptRe = regexp.MustCompile(`(?s)<script>(.*?)</script>`)
func inlineScriptHashes(page []byte) []string {
var out []string
for _, m := range inlineScriptRe.FindAllSubmatch(page, -1) {
sum := sha256.Sum256(m[1])
out = append(out, "'sha256-"+base64.StdEncoding.EncodeToString(sum[:])+"'")
}
return out
}
// pageCSP is the policy for one HTML page.
func pageCSP(page []byte) string {
script := append([]string{"'self'"}, inlineScriptHashes(page)...)
return strings.Join([]string{
"default-src 'self'",
"script-src " + strings.Join(script, " "),
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data:",
"connect-src 'self'",
"object-src 'none'",
"base-uri 'none'",
"form-action 'self'",
frameOnlyCSP,
}, "; ")
}