What's the Purpose of the HTML "Nonce" Attribute for Script and Style Elements

What’s the purpose of the HTML nonce attribute for script and style elements?

The nonce attribute lets you to “whitelist” certain inline script and style elements, while avoiding use of the CSP unsafe-inline directive (which would allow all inline script and style), so you still retain the key CSP feature of disallowing inline script/style in general.

So the nonce attribute is way to tell browsers the inline contents of a particular script or style element weren’t injected into the document by some (malicious) third party, but were instead put in the document intentionally by whoever controls the server the document is served from.


The Web Fundamentals Content Security Policy article’s If you absolutely must use it ... section has a good example of how to use the nonce attribute, which amounts to the following steps:

  1. For each request your web server gets for a particular document, have your backend make a random base64-encoded string of at least 128 bits from a cryptographically secure random number generator; e.g., EDNnf03nceIOfn39fn3e9h3sdfa. That’s your nonce.

  2. Take the nonce generated in step 1, and for any inline script/style you want to “whitelist”, make your backend code insert a nonce attribute into the document before it’s sent over the wire, with that nonce as the value:

     <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">…</script>
  3. Take the nonce generated in step 1, prepend nonce-, and make your backend generate a CSP header with that among the values of the source list for script-src or style-src:

     Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

So the mechanism of using a nonce is an alternative to instead having your backend generate a hash of the contents of the inline script or style you want to allow, and then specifying that hash in the appropriate source list in your CSP header.


Note: browsers don’t (can’t) check that the nonce values which servers send actually change between page requests; and so, it’s possible — though totally inadvisable — to skip 1 above and not have your backend do anything dynamically for the nonce, in which case you could just put a nonce attribute with a static value into the HTML source of your doc, and send a static CSP header with that same nonce value.

But the reason you’d not want to use a static nonce in that way is, it’d pretty much defeat the entire purpose of using the nonce at all to begin with — because, if you were to use a static nonce like that, at that point you might as well just be using unsafe-inline.


As far as which elements are “nonceable”: The CSP spec currently restricts browsers to checking nonces only for script and style elements. Here are the spec details:

  • In https://w3c.github.io/webappsec-csp/#match-element-to-source-list, see step 2:
    If type is "script" or "style", and § 6.6.3.1 Is element nonceable? returns "Nonceable"…

  • At https://w3c.github.io/webappsec-csp/#is-element-nonceable, the Is element nonceable? algorithm itself doesn’t check just for script/style elements; but the only place the spec calls that from is the part cited above, which restricts it to script/style. So if you put a nonce on any other element, the spec requires browsers to ignore it.

Which directive is better between nonce and hash for CSP header to avoid unsafe-inline?

  1. But since all the HTML is static how can I add a new nonce to the script tags for every request (as is the actual case for nonce)?

A 'hash value' is commonly used, since it is difficult to dynamically insert the nonce="value" attribute into script and style tags every time the page is reloaded.

However for the Nginx web server there is a solution using a server filter (ngx_http_sub_module or ngx_http_substitutions_filter_module).

In the static HTML you use some "unique string":

<script nonce="@@=AAABBBCCCZZZ=@@"></script>

which is replaced by the server's filter to a server generated value ($request_id):

location / {
subs_filter @@=AAABBBCCCZZZ=@@ $request_id;
}

But these modules may be not built into Nginx server by default and you have to rebuild server.


  1. Is providing hash of all the required scripts and styles better than nonce for such case?

The 'hashe-value' uses mostly in SPA (Single Page Apps) where you have no possibility to refresh nonce value. But it's hard to manage CSP with a lot of hashes when you change code and need to replace some hashes by a new ones.

Therefore, in the case of server-side rendering, the 'nonce value' is more often used. Moreover 'nonce value' can be used not only for inline scripts, but for external ones too.

Also see the comment about JS frameworks below - these can be incompatible with 'nonce-value' or 'hash-value'.


  1. If I refactor the code for all HTML to remove all the inline scripts from the body tag and add the code in a single script tag in head, does that mean that the inline scripts are eliminated?

Negative. CSP counts as inline scripts not only the <script> tags but also built-in tags event handlers like <button onClick='...'> and javascript-navigation kind of <a href='void(0)'>.

Also some JS frameworks like jQuery require mandatory 'unsafe-inline' or 'unsafe-eval' and can be incompatible with 'nonce-value' or 'hash-value'.

jQuery is compatible with 'nonce-value' since v3.5.0. But jQuery hardly compatible with a 'hash-value' because of dynamically generates main script and inserting it into <head> section.

What does the CSP nonce implementation look like?

React applications is a SPA(Single Page Application) so content is loaded using XMLHttpRequest() and inserted without page reloading. Therefore 'nonce-value' is not used since you have no way to generate a new 'nonce' each time page refreshing.

The 'nonce' can be used when SSR (Server Side Rendering), in this case server can gererate fresh 'nonce' value and insert it into HTML code been sent (into <script nonce='value'>, <script src ='some_url' nonce='value'>, <style nonce='value'> and <link href='some_url' nonce='value' rel='stylesheet'>).

For this reason, React applications use 'hash-value' to allow inline scripts and styles.

For example react-static-plugin-csp-meta-tags package adds a CSP meta tag to your html files and adding hashes for all inline script and style tags.

Add nonce to script tag

I ran your code on this stackoverflow page, and it worked.

I think the problem you're having is that you're expecting to see the nonce as an attribute of the script tag, but it's only available in javascript as a property.

The tag looks like this

<script type="text/javascript" charset="utf-8" async="" src="url"></script>

But if you run

console.log(document.getElementsByTagName('script')[0].nonce)

it will show "random-string"

The reason is security. See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce#accessing_nonces_and_nonce_hiding. Specifically

For security reasons, the nonce content attribute is hidden (an empty
string will be returned).

The nonce property is the only way to access nonces:

How to associate nonce attribute random id with inline JavaScript

https://www.w3.org/TR/CSP3/#security-nonces says that you should generate a unique value every time you submit your policy. This means that you can reuse the nonce throughout the document. On the next page load you need a new nonce.

Not able to apply nonce attribute to my inline css

for inline script you want to “whitelist”, make your backend code insert a nonce attribute into the document before it’s sent over the wire.

 <script nonce="EDNnf07nceIrfp39fn3e9h3sdfa">...</script>


Related Topics



Leave a reply



Submit