google / go-safeweb

Secure-by-default HTTP servers in Go.
Apache License 2.0
1.43k stars 81 forks source link

Allow JS in static HTML files #291

Open empijei opened 3 years ago

empijei commented 3 years ago

Issue

Currently any HTML file served as a static file will not run any JS, as we do not inject nonces in static files.

We should provide a way to make this work.

Proposal

We can have the htmlinject plugin (or a separate one) expose a function that accepts a trusted source or an embedded FS and performs two checks: 1) Validates that all scripts are inline (<script src= doesn't work with hashes on safari and inline event handlers hashes are not widely supported) 2) Extract hashes and pre-computes the arguments to pass to the CSP plugin in order for stuff to work (e.g. strict-dynamic will likely be necessary in these cases).

Alternative

Use nonces: when we load the static files we put placeholders for nonces, and we bytes.Replace the placeholder when we serve them. This is secure as static content can't contain injections, but nullifies caching capabilities.

Potential improvement

We apply the proposal above, but when we encounter a script that is not inline we rewrite it as an inline script that adds a new script element to the dom, and use the hash for the loader in CSP. The loader script could even be just one loader to make this more efficient, like this tool does, and we could enhance this to carry over async attributes instead of defaulting to false. Example:

When we see:

<script src="foo.js"></script>

We render:

<script>
var s = document.createElement('script');
s.src = "foo.js";
s.async = false;
document.body.appendChild(s);
</script>

And the hash for that script would be easy to compute.