Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple CSP headers restriction is unclear #215

Closed
ameshkov opened this issue May 12, 2017 · 19 comments
Closed

Multiple CSP headers restriction is unclear #215

ameshkov opened this issue May 12, 2017 · 19 comments

Comments

@ameshkov
Copy link

We're struggling to understand what does that clause mean in the documentation:

https://www.w3.org/TR/CSP2/#content-security-policy-header-field

A server MUST NOT send more than one HTTP header field named Content-Security-Policy with a given resource representation.

A server MAY send different Content-Security-Policy header field values with different representations of the same resource or with different resources.

Upon receiving an HTTP response containing at least one Content-Security-Policy header field, the user agent MUST enforce each of the policies contained in each such header field.

As I understand, the first part means that server must not send multiple CSP headers in a single response, while the third part implies, that it could.

I see, that in the draft MUST NOT was replaced with SHOULD NOT, but anyway, it stays unclear.

@annevk
Copy link
Member

annevk commented May 12, 2017

The third is a requirement on the client.

@ameshkov
Copy link
Author

@annevk even though server is restricted from sending multiple headers, client must be ready for it?

@annevk
Copy link
Member

annevk commented May 12, 2017

Basically. There's a lot of that in web platform standards.

@ameshkov
Copy link
Author

Got it, thank you for the clarification!

@nevercast
Copy link

The server can send multiple CSP headers provided they are not for the same resource, you could send two headers, one for 'script-src' and one for 'font-src', but not two for 'font-src'. That is my interpretation.

@shekyan
Copy link
Contributor

shekyan commented Jul 20, 2018

Server can send multiple Content-Security-Policy headers, with overlapping directives. Browser will enforce them individually, which is the same as union merging them.

This paragraph provides more details.

Here is an example.

@cnsgithub
Copy link

cnsgithub commented Jul 24, 2018

@shekyan That's interesting. I just stumbled upon some contradictory behavior in Chrome: I am generating Content-Security-Policy headers containing script-src directives dynamically on the fly. When initially visiting a web page script-src 'nonce-123' will be sent to the user agent. Then an ajax operation is performed and the XHR response contains a <script nonce="456 /> in data and script-src 'nonce-456' in header. However, Chrome refuses the latter script claiming that it violates against the initial policy. What I expected the browser to do was indeed union merging, however it seems to me that at least Chrome does intersection merging. Can you confirm this? (At the moment this is a show-stopper when trying to integrate CSP support in a JSF library.)

Union merging: https://cspvalidator.org/#headerValue%5B%5D=script-src+'nonce-123'&headerValue%5B%5D=script-src+'nonce-456'&strategy=union

Intersection merging: https://cspvalidator.org/#headerValue%5B%5D=script-src+'nonce-123'&headerValue%5B%5D=script-src+'nonce-456'&strategy=intersection

And btw @ameshkov, I agree with you, that it is in no way clearly defined.

@nevercast
Copy link

nevercast commented Jul 25, 2018

I am also seeing this behavior on my own site, where I specified default and script-src across two different headers (as it was easier to implement on my edge node), the second header seems to be ignored as the script is denied regardless of my second header.

I've tried to use cspvalidator, but it doesn't seem to give me much information on what union or intersect is doing.

My attention however, has been drawn to the semicolons at the end.

Content-Security-Policy: default-src 'self' http://example.com http://example.net;
                         connect-src 'none';
Content-Security-Policy: connect-src http://example.com/;
                         script-src http://example.com/

I suspect that the browser concatenates the CSP values, and ; deliminates.

Also regarding default

Interactions between the default-src and other directives SHOULD be given special consideration when combining policies. If none of the policies contains a default-src directive, adding new src directives results in a more restrictive policy. However, if one or more of the input policies contain a default-src directive, adding new src directives might result in a less restrictive policy, for example, if the more specific directive contains a more permissive set of allowed origins.

We should expect that script-src is respected when provided with default-src.

One sensible policy combination algorithm is to start by allowing a default set of sources and then letting individual upstream resource owners expand the set of allowed sources by including additional origins. In this approach, the resultant policy is the union of all allowed origins in the input policies.

Another sensible policy combination algorithm is to intersect the given policies. This approach enforces that content comes from a certain whitelist of origins, for example, preventing developers from including third-party scripts or content in violation of organizational standards and practices. In this approach, the combination algorithm forms the combined policy by removing disallowed hosts from the policies supplied by upstream resource owners.

Reading this, I'm not sure that the specification requires either union or intersection. Just implement one of them and you meet the spec. Which leaves it up to browser implementation.

I found this https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-calzavara.pdf which is some sec talk about this issue, but there is no CSP-Union headers or anything in HTTP spec. (This is probably a useless link, only bother reading it if you like reading)

I've looked at Google and Mozilla websites and find nothing of value regarding the CSP implementation. Can anyone gather anything concretely from the CSP spec that tells in certain words what the browsers must do when combining policies?

@nevercast
Copy link

This section further details the policy processing: https://www.w3.org/TR/CSP2/#enforce
The means of enforcing every resource is detailed here: https://www.w3.org/TR/CSP2/#directives

@cnsgithub How are you running your script via ajax? I didn't think a browser executes a script that is simply requested. What is the Content-Type of the response? Are you trying to use JSONP?

@cnsgithub
Copy link

cnsgithub commented Jul 25, 2018

@nevercast The script contained in the XHR response is executed by means of jquery's replaceWith that internally uses the DOMEval function:

    var preservedScriptAttributes = {
        type: true,
        src: true,
        noModule: true
    };

    function DOMEval( code, doc, node ) {
        doc = doc || document;

        var i,
            script = doc.createElement( "script" );

        script.text = code;
        if ( node ) {
            for ( i in preservedScriptAttributes ) {
                if ( node[ i ] ) {
                    script[ i ] = node[ i ];
                    script.setAttribute( i, node[ i ] );
                }
            }
        }
        doc.head.appendChild( script ).parentNode.removeChild( script );
    }

As you can see this function is currently buggy and needs to be fixed by adding the nonce attribute to the list of preservedScriptAttributes (see jquery/jquery#3541 for details).

However, fixing this is not sufficient since the Content-Security-Policy script-src directive that comes along with the XHR response gets discarded by the browser.

Here is an example of how it's working currently without CSP: https://www.primefaces.org/showcase/ui/ajax/process.xhtml. To test it just enter a surname and click the This Surname button. The XHR response will look like (shortened):

<?xml version='1.0' encoding='UTF-8'?>
<partial-response><changes><update id="j_idt638:grid"><![CDATA[<table id="j_idt638:grid" cellpadding="5" style="margin-bottom:10px">
...
<tr>
<td><label id="j_idt638:j_idt640" class="ui-outputlabel ui-widget" for="j_idt638:surname">Surname:<span class="ui-outputlabel-rfi">*</span></label></td>
<td><input id="j_idt638:surname" name="j_idt638:surname" type="text" value="test" aria-required="true" class="ui-inputfield ui-inputtext ui-widget ui-state-default ui-corner-all" data-p-label="Surname" data-p-rmsg="Surname is required." data-p-required="true" /><script id="j_idt638:surname_s" type="text/javascript">PrimeFaces.cw("InputText","widget_j_idt638_surname",{id:"j_idt638:surname"});</script></td>
</tr>
</tbody>
</table>
]]></update><update id="j_id1:javax.faces.ViewState:0"><![CDATA[468100927453503308:-4747695267725471375]]></update></changes></partial-response>

Notice the script block that gets executed by jquery's replaceWith as mentioned above.

What I am trying to do now to get CSP support integrated in PrimeFaces JSF library is to dynamically add nonce attributes to all script blocks, externalize JS event handlers and so on.

The script block would then look like:

<script id="j_idt638:surname_s" type="text/javascript" nonce="456">PrimeFaces.cw("InputText","widget_j_idt638_surname",{id:"j_idt638:surname"});</script>

And the response header would be Content-Security-Policy: script-src 'nonce-456'.

However, even when using the fixed jquery DOMEval function the browser would keep refusing the script because Content-Security-Policy: script-src 'nonce-123' would have been sent before in a previous response.

Hence the question how browsers must behave according to the specification? And what would be the proposed way for framework developers to get CSP working in highly dynamic applications where scripts cannot be predicted at the start (i.e. when sending the very first response)? I think JavaServer Faces is just one prominent example. Non-union merging of CSP directives would knock out those frameworks trying to support CSP.

Any help would be appreciated.

@cnsgithub
Copy link

Can someone give me a hint if it would be safe to generate a unique nonce per session and reuse it for all upcoming script blocks? At least browsers seem not to refuse those scripts?

@nevercast
Copy link

@cnsgithub Provided the only script that is running when the page is loaded is your own, and you're downloading your script over HTTPS so that it can't be intercepted. It should be "safe" (Understanding the limitations). Keep in mind, any script running on your page could inspect the nonce and drop it in their own, but they would have to be running their code already (and if they are running already, the nonce is practically irrelevant).

Disclaimer: Not a security professional.

@arturjanc
Copy link

In a nutshell: you can set multiple Content-Security-Policy headers on the response and each of the policies will be enforced separately. If both policies have a script-src directive, any script on the page has to be allowed by each of the policies -- if either policy prohibits the script from executing, it will be blocked. This means that if you have two CSPs which specify a nonce, both headers need to specify the same nonce value, or otherwise no script will be able to run, e.g.

Content-Security-Policy: script-src 'nonce-abc123' 'self' http://example.org
Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic'

Specifically, for the JSF case you're talking about, the core issue is that the application wants to execute inline scripts which are fetched in a separate request, and therefore by default will have a different nonce in the markup. One workaround for this scenario is to have the original document send its current nonce value to the server which generates the <partial-response>. The server can then emit markup using the nonce supplied by the client (after validating it to make sure malicious values don't result in an injection), and when the client receives the response, the nonce will match the nonce in the CSP of the original document. I would recommend against reusing the same nonce for the whole user session; since scriptless attacks are still possible (e.g. using CSS selectors to leak values of HTML attributes), this approach would very likely be bypassable by attackers.

Note that this discussion is probably not a great fit for a spec bug, I'd suggest moving it to Stack Overflow or a similar forum (I'm happy to chime in there in more detail).

@cnsgithub
Copy link

@arturjanc Would you like to chime in again at stackoverflow?

@briansmith
Copy link

briansmith commented May 3, 2019

A server MUST NOT send more than one HTTP header field named Content-Security-Policy with a given resource representation.

I don't understand why the specification says that. The CSP header is defined to be a comma-delimited field, which means:

Content-Security-Policy: a
Content-Security-Policy: b
Content-Security-Policy: c

means exactly the same thing as:

Content-Security-Policy: a, b, c

@cnsgithub
Copy link

means exactly the same thing as:

Nope.

@ceckoslab
Copy link

I am working on a middleware that attaches nonces on CSP headers. I started testing with multiple CSP headers and the spec didn't look very clear for me. I experimented and figured out some things also @arturjanc answer brought some clarity about how nonces should be attached.

While testing I faced an issue when returning those 2 headers back to the browser:

Content-Security-Policy: script-src 'self' 'unsafe-inline';
Content-Security-Policy: script-src http://*.example.com;

I tested with an inline script:

<script>alert("I am allowed script")</script>

This didn't fire the alert. I also tried changing the order of the headers but no luck. Is the correct way to serve:

Content-Security-Policy: script-src 'self' 'unsafe-inline';
Content-Security-Policy: script-src 'self' 'unsafe-inline' http://*.example.com;

Interesting to hear your opinion @cnsgithub and @shekyan

@koto
Copy link
Member

koto commented May 11, 2020

All policies specified are applied separately, in order for a request (or response, or inline behavior) not to be blocked, it would need to be allowed by each policy - this matches what you're experiencing. This is described at https://www.w3.org/TR/CSP3/#multiple-policies and in the respective algorithms in the spec (e.g. https://www.w3.org/TR/CSP3/#should-block-request).

@3dyd
Copy link

3dyd commented Jul 20, 2021

means exactly the same thing as:

Nope.

Could you elaborate? From RFC 7230 #3.2.2:

A sender MUST NOT generate multiple header fields with the same field
name in a message unless either the entire field value for that
header field is defined as a comma-separated list [i.e., #(values)]
or the header field is a well-known exception (as noted below).

And "below" is noted only Set-Cookie header. So CSP is another exception in this case?

UPD: sorry for bothering. I've found the answer myself. CSP header format is the following:

Content-Security-Policy = 1#serialized-policy

which expands to this:

Content-Security-Policy = serialized-policy *( optional-ascii-whitespace "," optional-ascii-whitespace serialized-policy )

so, while @briansmith comment is correct, there is no contradiction in the spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants