W3C home > Mailing lists > Public > public-webappsec@w3.org > September 2013

Updated script hash proposal (non spec text)

From: Neil Matatall <neilm@twitter.com>
Date: Thu, 19 Sep 2013 15:46:07 -0700
Message-ID: <CAOFLtbiFEJzVecXR28hE5cL_t=1ywhh=zUwHTciW26NhphiDtQ@mail.gmail.com>
To: "public-webappsec@w3.org" <public-webappsec@w3.org>
Sorry for the long period of silence, I've been doing some evangelizing.

Script hashes will be another source expression. Per script hash, the
algorithm and digest length precede the actual hash value. e.g.:

script-src 'sha256-0byNO6Svx+EJYSy3Osvd2sBSyTAlqh+ClC7au33rgqE'

If a script hash source is specified and the user agent understands
it, the browser should ignore the 'unsafe-inline' directive for
backwards compatibility. Any inline script whose computed hash value
does not match a hash specified in the hash sources should not be
executed and an informative error message should be displayed
including the expected hash value.

If multiple hashing algorithms are specified in the CSP header, the
browser must compute all possible hashes for each inline script block.
If the computed hash matches any computed hash in the header with a
matching algorithm+digest length, the script should execute. There was
talk of limiting this to one algorithm per header, but CDNs complicate
things.

This is not meant to and should not support dynamic javascript. Hashes
should not be computed dynamically (at least not in production).

=== Computing hash values

base64encode(<hashing algorithm>(UTF-8(<content of script tag>)))

<script>
  alert(1);
</script>

base64encode(sha256(UTF-8("\n  alert(1);\n")))

=== Script-hash unobtrusive workflow (PoC)

Unfortunately, many online hashing services will strip
leading/trailing whitespace which is not what we want.

I wrote a quick and dirty method for computing all script-hashes on a page:

$.each($('script'), function(index, x) {
  console.log(CryptoJS.SHA256(x.innerHTML).toString(CryptoJS.enc.Base64));
});

Here's the equivilent openssl command:

openssl dgst -sha256 -binary | base64

I wrote a more thorough rails plugin and explained how it works in [1]
including a (low quality) video on how the developer workflow would
work: [2].

Essentially:
1) Find all inline scripts - search through the source code of any
file that could be rendered / displayed to a user.
2) Extract the content of each inline script, hash according to the algo above.
3) Store as Filename -> [hashes] mapping. In a configuration file, for example.
4) Any time a file is rendered, the corresponding hashes are added to
the CSP header.

I believe this can be built in to every framework and be unobtrusive.

[1] http://nmatatal.blogspot.com/2013/09/how-my-script-hash-poc-works.html
[2] http://www.youtube.com/watch?v=Bc2hvziTRxg

p.s. I support both script nonce and script hash, I think we need to
have both :-/
Received on Thursday, 19 September 2013 22:46:34 UTC

This archive was generated by hypermail 2.3.1 : Monday, 23 October 2017 14:54:02 UTC