Why should we include CSP headers in the HTTP response for an API?

3.1k Views Asked by At

OWASP recommends to use Content-Security-Policy: frame-ancestors 'none' in API responses in order to avoid drag-and-drop style clickjacking attacks.

However, the CSP spec seems to indicate that after the HTML page is loaded any other CSP rules in the same context would be discarded without effect. Which makes sense in my mental model of how CSP works but if OWASP recommends it then I'm sure missing something.

Can anyone explain how can a CSP header in a XHR request improve security, after the fact that the HTML page is already loaded and the "main" CSP already evaluated? How that works in the browser?

3

There are 3 best solutions below

1
On BEST ANSWER

how can a CSP header in a XHR request improve security, after the fact that the HTML page is already loaded and the "main" CSP already evaluated?

You are right, browsers use CSP from main page and just ignore the CSP header sent along with the XHR requests.

But you haven't considered the second scenario - the API response is open in the browser's address bar or in a frame. In this case, cookies will be available to the response page, and if XSS is detected in the API (as, for example, in the PyPI simple endpoint API), then the user's confidential data may be available to an attacker.
Therefore, it is better to protect API responses with the "default-src `none" policy, as well as 404/403/500, etc pages.

1
On

The frame-ancestors 'none' directive will indicate to the browser on page load that it should not be rendered in a frame (including frame, iframe, embed, object, and applet tags). In other words the policy does not allow it to be framed by any other pages.

The CSP header for the API or page is read at load. It is not something that happens after the fact. The "main" CSP isn't pertinent because it's the URI in the frame that's sending the CSP for itself over. The browser simply honors the frame-ancestor 'none' request by that URI

The frame-ancestors directive restricts the URLs which can embed the resource using frame, iframe, object, or embed. Resources can use this directive to avoid many UI Redressing [UISECURITY] attacks, by avoiding the risk of being embedded into potentially hostile contexts.

References
CSP frame-ancestors
Clickjacking Defense Cheat Sheet
Content Security Policy
Web Sec Directive Frame Ancestors

0
On

Can anyone explain how can a CSP header in a XHR request improve security, after the fact that the HTML page is already loaded and the "main" CSP already evaluated? How that works in the browser?

Adding to the correct answer by granty above, Frames are commonly used for CSP bypasses. If a frame was allowed in a page (not blocked by the CSP), the frame has it's own CSP scope. So if you create some API for data - you don't want to allow it to be set as a frame as it could be used for bypassing the original CSP (for data exfiltration as an example).

So you can block this vulnerability by setting Content-Security-Policy: frame-ancestors 'none';, and then your API will refuse to be framed.

See this article on bypassing CSP for more info. The POC uses a creative hack:

frame=document.createElement(“iframe”);
frame.src=”/%2e%2e%2f”;
document.body.appendChild(frame);

which in turn triggers the NGINX error code page that does not have any CSP set. Many production CSPs are vulnerable to this issue.

Since not setting a CSP on a framed page would essentially default to no CSP (everything is open), the article suggests:

CSP headers should be present on all the pages, event on the error pages returned by the web-server