Search code examples
javascriptcontent-security-policyinline-scripting

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


I have to add CSP header to a site. Issue is that this is a legacy code and there are lots of inline scripts and styles in the HTML. I cannot use 'unsafe-inline' as the aim is to actually make the site secure and this directive gives red flag in OWASP ZAP tool scan. I can refactor the HTML and JS code to some extent but I have few questions:

  1. I have tried a fixed nonce (just for POC) and hash way of whitelisting the inline scripts. 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)?

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

  3. 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? In short, does in count as inline script?


Solution

    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.