A Refined Content Security Policy
The Content Security Policy standard lets you define a list of the inline scripts, inline stylesheets, and subresources that your page permits to load. You can define a content security policy on each page to restrict the capabilities that an attacker would have should a content injection vulnerability exist on the page. If your page displays user-generated content (e.g. a user profile page that renders markup provided or influenced by its owner) then Content Security Policy can be used to restrict the capabilities of such content. Think of Content Security Policy as an insurance policy against coding mistakes.
The latest WebKit builds and Safari Technology Preview now support all of the features of the Content Security Policy Level 2 standard, including hashes for script and style elements.
<script> and <style> hashes
You can now permit an inline script or inline stylesheet to load by including a cryptographic hash of its content in your CSP. Hashes make it easy to adopt Content Security Policy on existing pages that must include inline scripts and inline stylesheets. Add the SHA-256, SHA-384 or SHA-512 hash of the content of each inline <script>
and <style>
on your page to your policy to allow these elements to load and prevent loading arbitrary inline scripts and inline stylesheets that were not properly escaped in user-generated content or, worse, injected by an attacker. Using hashes also allows you to avoid adding the risky 'unsafe-inline'
keyword to your policy.
Let’s look at an example. If you are using the latest WebKit build or Safari Technology Preview and you click here then you will see a white silhouette of the WebKit compass inside a blue square. Otherwise, you will see a blue square. Here is the CSP meta tag of the page:
<meta http-equiv="Content-Security-Policy" content="style-src 'self' 'sha256-G0CufoWPLTm5OhVU1OioVsF0ge/f2hWhXLJHu3QH0bQ='; img-src 'self'; default-src 'none'">
The inline stylesheet that loads the compass image has the hash source expression 'sha256-G0CufoWPLTm5OhVU1OioVsF0ge/f2hWhXLJHu3QH0bQ='
. It is allowed to load because its hash appears in the CSP meta tag.
We enhanced Web Inspector to show the CSP SHA-256 hash expression for a selected script or style element in the Node details pane.
This allows you to simply select the <script>
or <style>
in the Elements tab and copy and paste the “CSP Hash” from the Node details sidebar into your policy. This is how I obtained 'sha256-G0CufoWPLTm5OhVU1OioVsF0ge/f2hWhXLJHu3QH0bQ='
to use in the CSP meta tag above.
Browsers that support CSP , but do not support CSP hashes will refuse to load the inline stylesheet in the example. We can make the example backwards compatible with such browsers by adding 'unsafe-inline'
to our meta tag, as shown below. The added keyword will let these browsers load the content of all style elements on the page. Browsers that support CSP hashes will ignore the 'unsafe-inline'
keyword and perform hash comparisons.
<meta http-equiv="Content-Security-Policy" content="style-src 'self' 'sha256-G0CufoWPLTm5OhVU1OioVsF0ge/f2hWhXLJHu3QH0bQ=' 'unsafe-inline'; img-src 'self'; default-src 'none'">
WebKit supports all of the CSP Level 2 hash algorithms: SHA-256, SHA-384, and SHA-512. Even though your policy can have hashes that are a mix of different hash algorithms, using more than one hash algorithm can cause a browser to perform unnecessary hash computations. If you do make use of hashes then ensure that you add them to the most specific directive in your policy: script-src
for script elements and style-src
for style elements. Avoid adding hashes to the default-src
directive as it guarantees that a browser must compute the hash of- and perform hash comparisons for- each script and style element in the page.
More restrictive wildcard *
Historically a CSP policy was defined using a list of source expressions that matched the URLs of the subresources that the page permitted to load. The source expression *
is a wildcard expression that matches any URL that is not data: or blob:. WebKit further restricts the URL schemes that *
can match to prevent you from inadvertently permitting loads from these special schemes when such loads could be exploited by user-generated content or an attacker to run arbitrary code. This change is a backwards incompatible change and represents a careful balancing act between web compatibility and mitigating content injection vulnerabilities in sites that make use of *
. The advantage of this change is that it makes *
have a safer default behavior. The disadvantage is that you will need to explicitly add special schemes and custom protocols to your policy so as to permit loads from them. Here is a composite breakdown of the schemes that *
matches when it appears in the source list of various directives:
Directive | * matches |
---|---|
img-src | “scheme of the page”, http:, https:, data: |
media-src | “scheme of the page”, http:, https:, data:, blob: |
Anything else | “scheme of the page”, http:, https: |
We felt it was acceptable to allow *
to match data:
for images, and data:
and blob:
for media because JavaScript code embedded in URLs with these schemes will not execute when loaded via an <img>
, <video>
or <audio>
.
As aforementioned, you will need to explicitly whitelist any custom protocols in your policy if you develop an app that embeds a web view and loads resources over custom protocols. For example, if you wanted to restrict loading images to common schemes and your own custom protocol app: you would use:
<meta http-equiv="Content-Security-Policy" content="img-src * app:">
Feedback Welcome
As usual, report any bugs you find in Content Security Policy at bugs.webkit.org. For comments or questions, feel free to contact the WebKit team on Twitter at @WebKit or Jonathan Davis, our Web Technologies Evangelist at @jonathandavis. Enjoy!