subresource integrity

This commit is contained in:
Azareal 2020-07-31 15:33:29 +10:00
parent 26ad61057a
commit f502bf4f53
6 changed files with 63 additions and 64 deletions

View File

@ -4,6 +4,7 @@ import (
"bytes"
"compress/gzip"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
@ -43,6 +44,7 @@ type SFile struct {
BrData []byte
Sha256 string
Sha256I string
OName string
Pos int64
@ -306,9 +308,11 @@ func (l SFileList) JSTmplInit() error {
// Get a checksum for CSPs and cache busting
hasher := sha256.New()
hasher.Write(data)
checksum := hex.EncodeToString(hasher.Sum(nil))
sum := hasher.Sum(nil)
checksum := hex.EncodeToString(sum)
integrity := base64.StdEncoding.EncodeToString(sum)
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, integrity, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
DebugLogf("Added the '%s' static file.", path)
return nil
@ -336,7 +340,9 @@ func (l SFileList) Init() error {
// Get a checksum for CSPs and cache busting
hasher := sha256.New()
hasher.Write(data)
checksum := hex.EncodeToString(hasher.Sum(nil))
sum := hasher.Sum(nil)
checksum := hex.EncodeToString(sum)
integrity := base64.StdEncoding.EncodeToString(sum)
// Avoid double-compressing images
var gzipData, brData []byte
@ -370,7 +376,7 @@ func (l SFileList) Init() error {
}
}
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mimetype, f, f.ModTime().UTC().Format(http.TimeFormat)})
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, integrity, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mimetype, f, f.ModTime().UTC().Format(http.TimeFormat)})
DebugLogf("Added the '%s' static file.", path)
return nil
@ -425,9 +431,11 @@ func (l SFileList) Add(path, prefix string) error {
// Get a checksum for CSPs and cache busting
hasher := sha256.New()
hasher.Write(data)
checksum := hex.EncodeToString(hasher.Sum(nil))
sum := hasher.Sum(nil)
checksum := hex.EncodeToString(sum)
integrity := base64.StdEncoding.EncodeToString(sum)
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, integrity, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
DebugLogf("Added the '%s' static file", path)
return nil

View File

@ -21,11 +21,11 @@ type Header struct {
Title string
//Title []byte // Experimenting with []byte for increased efficiency, let's avoid converting too many things to []byte, as it involves a lot of extra boilerplate
NoticeList []string
Scripts []string
PreScriptsAsync []string
ScriptsAsync []string
Scripts []HScript
PreScriptsAsync []HScript
ScriptsAsync []HScript
//Preload []string
Stylesheets []string
Stylesheets []HScript
Widgets PageWidgets
Site *site
Settings SettingMap
@ -52,38 +52,33 @@ type Header struct {
ExtData ExtData
}
func (h *Header) AddScript(name string) {
type HScript struct {
Name string
Hash string
}
func (h *Header) getScript(name string) HScript {
if name[0] == '/' && name[1] == '/' {
} else {
file, ok := StaticFiles.GetShort(name)
if ok {
name = file.OName
} else {
file, ok := StaticFiles.GetShort(name)
if ok {
return HScript{file.OName,file.Sha256I}
}
}
}
return HScript{name,""}
}
func (h *Header) AddScript(name string) {
//log.Print("name:", name)
h.Scripts = append(h.Scripts, name)
h.Scripts = append(h.Scripts, h.getScript(name))
}
func (h *Header) AddPreScriptAsync(name string) {
if name[0] == '/' && name[1] == '/' {
} else {
file, ok := StaticFiles.GetShort(name)
if ok {
name = file.OName
}
}
h.PreScriptsAsync = append(h.PreScriptsAsync, name)
h.PreScriptsAsync = append(h.PreScriptsAsync, h.getScript(name))
}
func (h *Header) AddScriptAsync(name string) {
if name[0] == '/' && name[1] == '/' {
} else {
file, ok := StaticFiles.GetShort(name)
if ok {
name = file.OName
}
}
h.ScriptsAsync = append(h.ScriptsAsync, name)
h.ScriptsAsync = append(h.ScriptsAsync, h.getScript(name))
}
/*func (h *Header) Preload(name string) {
@ -91,14 +86,7 @@ func (h *Header) AddScriptAsync(name string) {
}*/
func (h *Header) AddSheet(name string) {
if name[0] == '/' && name[1] == '/' {
} else {
file, ok := StaticFiles.GetShort(name)
if ok {
name = file.OName
}
}
h.Stylesheets = append(h.Stylesheets, name)
h.Stylesheets = append(h.Stylesheets, h.getScript(name))
}
// ! Experimental

View File

@ -106,31 +106,31 @@ func tmplInitUsers() (*User, *User, *User) {
return &u, &u2, &u3
}
func tmplInitHeaders(user, user2, user3 *User) (*Header, *Header, *Header) {
func tmplInitHeaders(u, u2, u3 *User) (*Header, *Header, *Header) {
header := &Header{
Site: Site,
Settings: SettingBox.Load().(SettingMap),
Themes: Themes,
Theme: Themes[DefaultThemeBox.Load().(string)],
CurrentUser: user,
CurrentUser: u,
NoticeList: []string{"test"},
Stylesheets: []string{"panel.css"},
Scripts: []string{"whatever.js"},
PreScriptsAsync: []string{"whatever.js"},
ScriptsAsync: []string{"whatever.js"},
Stylesheets: []HScript{HScript{"panel.css",""}},
Scripts: []HScript{HScript{"whatever.js",""}},
PreScriptsAsync: []HScript{HScript{"whatever.js",""}},
ScriptsAsync: []HScript{HScript{"whatever.js",""}},
Widgets: PageWidgets{
LeftSidebar: template.HTML("lalala"),
},
}
buildHeader := func(user *User) *Header {
buildHeader := func(u *User) *Header {
head := &Header{Site: Site}
*head = *header
head.CurrentUser = user
head.CurrentUser = u
return head
}
return header, buildHeader(user2), buildHeader(user3)
return header, buildHeader(u2), buildHeader(u3)
}
type TmplLoggedin struct {

View File

@ -5,6 +5,7 @@ import (
"bytes"
"crypto/sha256"
"database/sql"
"encoding/base64"
"encoding/hex"
"errors"
htmpl "html/template"
@ -282,9 +283,11 @@ func (t *Theme) AddThemeStaticFiles() error {
// Get a checksum for CSPs and cache busting
hasher := sha256.New()
hasher.Write(data)
checksum := hex.EncodeToString(hasher.Sum(nil))
sum := hasher.Sum(nil)
checksum := hex.EncodeToString(sum)
integrity := base64.StdEncoding.EncodeToString(sum)
StaticFiles.Set(StaticFiles.Prefix+t.Name+path, &SFile{data, gzipData, brData, checksum, StaticFiles.Prefix + t.Name + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
StaticFiles.Set(StaticFiles.Prefix+t.Name+path, &SFile{data, gzipData, brData, checksum, integrity, StaticFiles.Prefix + t.Name + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
DebugLog("Added the '/" + t.Name + path + "' static file for theme " + t.Name + ".")
return nil

View File

@ -44,14 +44,14 @@ func doPush(w http.ResponseWriter, h *c.Header) {
sb.Reset()
}*/
sb.Grow((slen1 * (len(h.Scripts) + len(h.ScriptsAsync))) + ((slen2 + 7) * len(h.Stylesheets)))
push := func(in []string) {
for i, path := range in {
push := func(in []c.HScript) {
for i, s := range in {
if i != 0 {
sb.WriteString(",</s/")
} else {
sb.WriteString("</s/")
}
sb.WriteString(path)
sb.WriteString(s.Name)
sb.WriteString(">;rel=preload;as=script")
}
}
@ -60,13 +60,13 @@ func doPush(w http.ResponseWriter, h *c.Header) {
push(h.ScriptsAsync)
if len(h.Stylesheets) > 0 {
for i, path := range h.Stylesheets {
for i, s := range h.Stylesheets {
if i != 0 {
sb.WriteString(",</s/")
} else {
sb.WriteString("</s/")
}
sb.WriteString(path)
sb.WriteString(s.Name)
sb.WriteString(">;rel=preload;as=style")
}
}
@ -100,11 +100,11 @@ func doPush(w http.ResponseWriter, h *c.Header) {
sb.Reset()
}*/
sb.Grow(6 * (len(h.Scripts) + len(h.ScriptsAsync) + len(h.Stylesheets)))
push := func(in []string) {
for _, path := range in {
push := func(in []c.HScript) {
for _, s := range in {
//fmt.Println("pushing /s/" + path)
sb.WriteString("/s/")
sb.WriteString(path)
sb.WriteString(s.Name)
err := pusher.Push(sb.String(), nil)
if err != nil {
break

View File

@ -3,16 +3,16 @@
<head>
<title>{{.Title}} | {{.Header.Site.Name}}</title>
{{range .Header.Stylesheets}}
<link href="{{.}}"rel="stylesheet"type="text/css">{{end}}
<link href="{{.Name}}"rel="stylesheet"type="text/css"{{if .Hash}}integrity="sha256-{{.Hash}}"{{end}}>{{end}}
{{range .Header.PreScriptsAsync}}
<script async src="{{.}}"></script>{{end}}
<script async src="{{.Name}}"{{if .Hash}}integrity="sha256-{{.Hash}}"{{end}}></script>{{end}}
{{if .CurrentUser.Loggedin}}<meta property="x-mem"content="1">{{end}}
<script src="{{res "init.js"}}"></script>
{{range .Header.ScriptsAsync}}
<script async src="{{.}}"></script>{{end}}
<script async src="{{.Name}}"{{if .Hash}}integrity="sha256-{{.Hash}}"{{end}}></script>{{end}}
<script src="{{res "jquery-3.1.1.min.js"}}"></script>
{{range .Header.Scripts}}
<script src="{{.}}"></script>{{end}}
<script src="{{.Name}}"{{if .Hash}}integrity="sha256-{{.Hash}}"{{end}}></script>{{end}}
<meta name="viewport"content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
{{if .Header.MetaDesc}}<meta name="description"content="{{.Header.MetaDesc}}">{{end}}
{{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}}