CSP hash on scripts, SRI and 'require-sri-for' - no more 'self'!

In Content Security Policy (CSP) version 3 it will be possible to allow hash expression on script files, that is .js-files. This was not possible in previous versions of CSP where hashes only could be used on inline scripts.

CSP3 allows hash expressions to match external scripts, by relying on SRI as underlying infrastructure. That is, given Content-Security-Policy: script-src 'sha256-abc123' 'sha512-321cba', <script integrity="sha256-abc123" ...></script> will be allowed.

Source: https://www.chromestatus.com/feature/4626666856906752

Now that Subresource Integrity (SRI) is supported in most browsers we can combine the CSP directive require-sri-for, CSP hashes to match script files and using SRI and create a much safer deployment of CSP.


Why 'self' is not enough

The attribute 'self' is used to whitelist resources that is used on self; the current origin (protocol, domain, port) and an origin can contain several attack vectors. One attack vector could be that a user is allowed to upload files, and if this file uploading feature is not safe enough, it can be possible for an attacker to upload .js files to be executed under the current origin (for example https://example.com/user/uploads/image.jpg.js will have complete control over https://example.com).

It has been discussed that 'self' should allow paths, for example
script-src 'self:/scripts/' - but this is not possible, or a good [practical] solution. ¹

In the example above it would still be possible for an attacker to bypass the CSP if the attacker could upload files on the path /scripts/ - so what is a good solution on this problem? ²

Hashes is more secure than origins

In the upcoming release of CSP3 it will be possible to use hashes to whitelist scripts instead. An example would be:

script-src 'sha256-abcdefghijklmnopqrstuvwxyz='

<script src="/file.js" integrity="sha256-abcdefghijklmnopqrstuvwxyz="></script>

to match the script file with the SHA256 hash "abcdefghijklmnopqrstuvwxyz=". If CSP can whitelist hashes instead of origins and paths, the attacker will have a really hard time bypassing the CSP.

The attacker must first be able to insert the hash of the script to be whitelisted in the CSP (extremely unlikely) and then find an injection point for where the script can be executed. Still, this is not all. Because the use of require-sri-for, the attacker also need to inject a full <script>-tag containing the integrity-attribute.

Combining CSP hashes with origin and paths

It's possible [according to Mike West] to combine an origin with CSP hashes.

So in other words, script-src 'self' 'sha256-abcdefghijklmnopqrstuvwxyz=' would only allow scripts from 'self' with the hash abcdefghijklmnopqrstuvwxyz= - This will reduce the attack surface even more! Of course you can include paths to reduce the attack even further:

script-src https://cdn.example.com/scripts/ 'sha256-abcdefghijklmnopqrstuvwxyz='


Conclusion

Finally we can use hashes to match scripts instead of origins! We can also combine hashes and origins to reduce the attack surface by so much that an attacker need to chain several vulnerabilities to bypass the CSP.

However, if the scripts suffers from DOM XSS or is an Angular or JSONP endpoint, it could be possible for an attacker to execute scripts. Using hashes only takes care of the problem with the scenario that an attacker can upload files under the current origin, for example via a broken uploading feature.

If you are using Angular or JSONP endpoints, it would be a good idea to host these files under another origin. If you are hosting and whitelisting vulnerable endpoints under 'self', hashes won't save you.


[1]: You can use paths on 'self' to whitelist paths, but if an attacker can upload files to that path, a bypass is practical possible. Vulnerabilities such as remote code execution could allow attackers to bypass the CSP and pivot.

[2]: A good solution does not include whitelisting paths. A good solution is to whitelist hashes.