4

We've recently conducted a security review of our identity server website and one of the finding was about missing CSP header. We do have implementation of request filter to add CSP on a controller level. So my question is, is there any documentation out there recommending CSP for or against certain response? It seems overkill to include CSP in all responses.

Here is what I could find which makes sense for pages that have reference to 3rd party code like analytics. https://developers.google.com/web/fundamentals/security/csp

Even on a fully static website, which does not accept any user input, a CSP can be used to enforce the use of Subresource Integrity (SRI). This can help prevent malicious code from being loaded on the website if one of the third-party sites hosting JavaScript files (such as analytics scripts) is compromised.

Here are some responses I am questioning whether they are appropriate. Finding some documentation around it would help us justify whether or not it is worth adding them.

  • Non HTML response (json, css, etc...)
  • Redirect response (redirect 302)

3 Answers 3

3

First, note that "having a CSP" doesn't mean much by itself. The benefit of sending a CSP header depends on the specific rules (directives) it contains. One flawed directive may render the entire policy ineffective.

As @CBHacking outlined, the most important feature of CSPs is to reduce the viability/impact of content injection vulnerabilities (most notably XSS). Hence, CSP directives impose restrictions on active content, like the scripts, links and forms on an HTML document. By contrast, a page served as text/css, application/json or image/png is not (inter)active – it can't fetch, execute or link any additional resources that one may want to control with a CSP.

That said, a bit of an exception is frame-ancestors. Using this directive to disallow cross-origin framing can be useful even for some passive content, e.g. an API endpoint that returns sensitive data in an application/json response. Here, a crafty attacker may load the API URL in an iframe and use social engineering (or timing attacks) to extract the iframe's content. A directive like frame-ancestors 'none' would prevent this type of attack (as would X-Frame-Options: DENY of course).

This special case aside – No, your passive content doesn't need a CSP. There is no content behavior or interactions to be constrained.

(Maybe future extensions of the standard introduce additional use cases for a CSP on passive content.)

2
  • Choosing this as answer just because it also talk about @CBHacking points but I agree on both and in the end what I ended up doing is implementing less but more blanket policy none specific pages and more mindfully thought of policy on more interactive pages. May 12, 2021 at 22:42
  • @MichaelChan Sounds good! As with many features like security-related headers - implementing them only where it's necessary vs. site-wide often comes down to what keeps the application design straightforward and manageable.
    – Arminius
    May 12, 2021 at 22:48
2

CSP is only in effect on content that creates its own rendering context. In general, this means two kinds of content: HTML and SVG (other XML-based types, such as RSS/ATOM, might also support CSP in the few browsers that still support them; I'm not sure). These might be full pages, frames, iframes, objects, or (in the case of SVG) images. Note that it only impacts content that is rendered directly into the page; if the contents of an iframe are retrieved using XHR/Fetch and then dynamically inserted into the iframe's document, then CSP headers on the XHR/Fetch response will not have any effect (CSP in HTML meta tags will still be effective when the iframe loads).

Additionally, subcontexts inherit CSP from their parent context. This means that you don't need to set a CSP for something like an SVG or iframe if you set it for the parent page. However, it's still a good idea; if there's an XSS vuln in a subcontext (yes, SVG can contain script!), an attacker would just navigate the victim to the SVG/iframe/etc. URL directly, rather than to the parent page, and you don't want them to be able to skip CSP that way. It's also possible to extend or override the parent context CSP, which is useful in some cases (e.g. allowing inline styles in SVG but not elsewhere).

Mind you, there's no harm - aside from slightly more network traffic - in sending CSP with every response. But if you want to reduce it, HTML and SVG are the areas to focus on. Redirects and error pages with no content are probably fine to skip, but if there's any HTML content, it can use SVG. If you want to go extremely minimal for some reason, CSP can be safely skipped on any page that neither handles any user-sourced data (including stored data from the DB) nor includes any externally-sourced content (scripts, stylesheets, images, iframes, etc.). However, it's best to send CSP with such responses anyhow, as the content of those pages might change in the future and it's best to be already protected (especially at such minimal cost).

1

Let's define the correct technical terms and dispel some myths CSP about.

  1. Content Security Policy doesn't care about "active" or "interactive" content.
  • Is the script.js file (containing javascript) with " text/javascript" MIME an "active" content? But browsers just ignore any CSP header send with such script.js file (except Firefox, in case of workers).

  • Is the logo.gif with "image/png" MIME a "passive" content? Not at all (hello from polyglot GIF with built-in JS). But browsers ignore any CSP header send with such logo.gif file, CSP have no deal with that.

  • Is the mp3 an "interactive" content"? You can interact with that - to press stop/pause/loud/rewind, but CSP headers sent with MP3/MP4 file doesn't block it play.

  • Is style.css sent with MIME text/css or robots.txt with MIME plain/text "active" content? But if it contains a valid javascript code, it will be executed. And again, the CSP sent with such a file will be ignored by browsers.

  1. In parallel, is dispelled the second myth that MIME type have a meaning. Due to sniffing, browsers examine real files format and ignore MIME in most cases.

  2. There is not any "passive" content for browsers. Browsers render all content as HTML and apply CSP sent with any file. Open this robots.txt in Chrome and check browser console - you'll see that CSP blocks inline styles. Right mouse click -> "Inspect code" and you'll see an artificial HTML wrapper.

So what is governed by CSP and where does it work?

A security model under which CSP operates is based on origins and browsing contexts (as and a security model of Same Origin Policy).

Therefore, it only makes sense to publish Content Security Policy for a separate browsing context.

The top-level document creates a top-level browsing context, and <iframe> / <object> / <embed> tags create nested browsing contexts.
If you embed an image as <img src='logo.gif'> a separate browsing context is not created, therefore such image falls under CSP of embedding context.
If you embed an image as <iframe src='logo.gif'> a separate browsing context is created therefore such image can have own CSP header.

Note 1 that some browsing contexts can be non-isolated from point of view of CSP, and will inherits CSP of parent context.

Note 2 A frame-ancestors is the only directive that does not affect the viewing context that issued the CSP, but affects all its parent contexts. Arminius in his answer revealed the specifics of this directive including for non-HTML files.

Now topic starter can answered his questions byself:

Redirect response (redirect 302)

Redirect header does not create a browsing context so CSP is not supported creates a nested browsing context, therefore CSP header is applicable (Google publishes CSP on accounts.google.com page with redirect 302). Keep in mind that CSP have a deal with redirects, for instance in form-action directive. But behaviour is different in Chrome and Firefox.

Non HTML response (json, css, etc...)

Such responses be handled by parent HTML document (browsing context), therefore CSP header of sub-responses will be ignored (except script loaded for SharedWorkers in Firefox). But origins of request to get such responses is covered by CSP of this parent document.
If you open such "non HTML response" directly in address bar (separate browsing context), the CSP sent will apply but there is no reason to protect this context. Only parent page with the same origin can access such arctificially created HTML wrapper (maybe browser extensions can access too).

And now - a question for $1000: Do I need to publish CSP on pages with response codes 4xx and 5xx (Not Found and Internal Server Error, etc)?
Safety is never enough...

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .