Support CSP nonce for webv2. Closes #2127
This commit is contained in:
parent
acc9cd39a5
commit
2fdbb1e482
@ -2,6 +2,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -23,21 +24,23 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if isIndexRequest {
|
||||
renderIndexHtml(w)
|
||||
return
|
||||
}
|
||||
|
||||
// Set a cache control max-age header
|
||||
middleware.SetCachingHeaders(w, r)
|
||||
|
||||
nonceRandom, _ := utils.GenerateRandomString(5)
|
||||
|
||||
// Set our global HTTP headers
|
||||
middleware.SetHeaders(w)
|
||||
middleware.SetHeaders(w, fmt.Sprintf("nonce-%s", nonceRandom))
|
||||
|
||||
if isIndexRequest {
|
||||
renderIndexHtml(w, nonceRandom)
|
||||
return
|
||||
}
|
||||
|
||||
serveWeb(w, r)
|
||||
}
|
||||
|
||||
func renderIndexHtml(w http.ResponseWriter) {
|
||||
func renderIndexHtml(w http.ResponseWriter, nonce string) {
|
||||
type serverSideContent struct {
|
||||
Name string
|
||||
Summary string
|
||||
@ -48,6 +51,7 @@ func renderIndexHtml(w http.ResponseWriter) {
|
||||
Image string
|
||||
StatusJSON string
|
||||
ServerConfigJSON string
|
||||
Nonce string
|
||||
}
|
||||
|
||||
status := getStatusResponse()
|
||||
@ -74,6 +78,7 @@ func renderIndexHtml(w http.ResponseWriter) {
|
||||
Image: "/logo/external",
|
||||
StatusJSON: string(sb),
|
||||
ServerConfigJSON: string(cb),
|
||||
Nonce: nonce,
|
||||
}
|
||||
|
||||
index, err := static.GetWebIndexTemplate()
|
||||
|
@ -3,22 +3,14 @@ package middleware
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SetHeaders will set our global headers for web resources.
|
||||
func SetHeaders(w http.ResponseWriter) {
|
||||
// When running automated browser tests we must allow `unsafe-eval` in our CSP
|
||||
// so we can explicitly add it only when needed.
|
||||
inTest := os.Getenv("BROWSER_TEST") == "true"
|
||||
unsafeEval := ""
|
||||
if inTest {
|
||||
unsafeEval = `'unsafe-eval'`
|
||||
}
|
||||
func SetHeaders(w http.ResponseWriter, nonce string) {
|
||||
// Content security policy
|
||||
csp := []string{
|
||||
fmt.Sprintf("script-src 'self' %s 'sha256-B5bOgtE39ax4J6RqDE93TVYrJeLAdxDOJFtF3hoWYDw=' 'sha256-PzXGlTLvNFZ7et6GkP2nD3XuSaAKQVBSYiHzU2ZKm8o=' 'sha256-/wqazZOqIpFSIrNVseblbKCXrezG73X7CMqRSTf+8zw=' 'sha256-jCj2f+ICtd8fvdb0ngc+Hkr/ZnZOMvNkikno/XR6VZs='", unsafeEval),
|
||||
fmt.Sprintf("script-src '%s' 'self'", nonce),
|
||||
"worker-src 'self' blob:", // No single quotes around blob:
|
||||
}
|
||||
w.Header().Set("Content-Security-Policy", strings.Join(csp, "; "))
|
||||
|
@ -9,7 +9,7 @@ const tokenLength = 32
|
||||
|
||||
// GenerateAccessToken will generate and return an access token.
|
||||
func GenerateAccessToken() (string, error) {
|
||||
return generateRandomString(tokenLength)
|
||||
return GenerateRandomString(tokenLength)
|
||||
}
|
||||
|
||||
// generateRandomBytes returns securely generated random bytes.
|
||||
@ -27,12 +27,12 @@ func generateRandomBytes(n int) ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// generateRandomString returns a URL-safe, base64 encoded
|
||||
// GenerateRandomString returns a URL-safe, base64 encoded
|
||||
// securely generated random string.
|
||||
// It will return an error if the system's secure random
|
||||
// number generator fails to function correctly, in which
|
||||
// case the caller should not continue.
|
||||
func generateRandomString(n int) (string, error) {
|
||||
func GenerateRandomString(n int) (string, error) {
|
||||
b, err := generateRandomBytes(n)
|
||||
return base64.URLEncoding.EncodeToString(b), err
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { FC } from 'react';
|
||||
export const ServerRenderedHydration: FC = () => (
|
||||
<script
|
||||
id="server-side-hydration"
|
||||
nonce="{{.Nonce}}"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.configHydration = {{.ServerConfigJSON}};
|
||||
|
Loading…
Reference in New Issue
Block a user