client/web: use CSP hash for inline javascript

Calculate and set the hash of the one inline script we have in
index.html. That script is unlikely to change, so hardcoding the hash
seems fine for now.

Updates #10261
Updates tailscale/corp#16266

Signed-off-by: Will Norris <will@tailscale.com>
This commit is contained in:
Will Norris 2023-12-11 19:33:38 -08:00 committed by Will Norris
parent bc9b9e8f69
commit d2fbdb005d
2 changed files with 9 additions and 4 deletions

View File

@ -15,13 +15,14 @@
</noscript> </noscript>
<script type="module" src="/src/index.tsx"></script> <script type="module" src="/src/index.tsx"></script>
<script> <script>
// if this script is changed, also change hash in web.go
window.addEventListener("load", () => { window.addEventListener("load", () => {
if (!window.Tailscale) { if (!window.Tailscale) {
const rootEl = document.createElement("p") const rootEl = document.createElement("p")
rootEl.innerHTML = 'Tailscale was built without the web client. See <a href="https://github.com/tailscale/tailscale#building-the-web-client">Building the web client</a> for more information.' rootEl.innerHTML = 'Tailscale web interface is unavailable.';
document.body.append(rootEl) document.body.append(rootEl)
} }
}); })
</script> </script>
</body> </body>
</html> </html>

View File

@ -251,9 +251,13 @@ func (s *Server) serve(w http.ResponseWriter, r *http.Request) {
} }
if !s.devMode { if !s.devMode {
// This hash corresponds to the inline script in index.html that runs when the react app is unavailable.
// It was generated from https://csplite.com/csp/sha/.
// If the contents of the script are changed, this hash must be updated.
const indexScriptHash = "sha384-CW2AYVfS14P7QHZN27thEkMLKiCj3YNURPoLc1elwiEkMVHeuYTWkJOEki1F3nZc"
w.Header().Set("X-Frame-Options", "DENY") w.Header().Set("X-Frame-Options", "DENY")
// TODO: use CSP nonce or hash to eliminate need for unsafe-inline w.Header().Set("Content-Security-Policy", "default-src 'self'; img-src * data:; script-src 'self' '"+indexScriptHash+"'")
w.Header().Set("Content-Security-Policy", "default-src 'self'; img-src * data:")
w.Header().Set("Cross-Origin-Resource-Policy", "same-origin") w.Header().Set("Cross-Origin-Resource-Policy", "same-origin")
} }
} }