There is the Header X-Frame-Options, which is served by the webserver when you want to forbid (or limit) other sites from embedding your page into theirs, using an iframe.
But, is there a Header which tells the browser: "Don't allow any Iframe to be loaded on this page"?
There are, of course, headers which tell the browser which scripts, from which domain, is it allowed to execute, but I want something more generic: "don't allow any iframe, or only iframes from certain origins, to be loaded on this page".
Content-Security-Policy (CSP) can be used to restrict content on pages, including iframes. Specifically, the frame-src directive. If you set the following HTTP header, no iframes will be allowed on your page.
Content-Security-Policy: frame-src 'none'
If you want to only allow iframes from specific origins you could do the following to allow iframes from example.com, and all subdomains:
Content-Security-Policy: frame-src http://*.example.com
You can also set the CSP policy via meta tag.
Related
If I'm loading another site in an iFrame do the Content Security Policy Headers of that site have any affect on whether the site gets blocked?
e.g. if I open www.google.com in an iFrame is there any interaction between the CSP header settings on my site and the ones on google.com? Or would Google's CSP only affect what they're trying to load in the iFrame.
Of course if google had their own iFrames they'd need CSP headers to allow any 3rd party content to load. But do my CSP headers have any affect on Google's after google.com starts to load? If Google tried to load youtube.com in an iFrame and I didn't include youtube.com in my CSP whitelist would that work?
Sorry if this is a silly question, I'm trying to wrap my head around iFrames. What I'm wondering is if I need to worry about the CSP settings on the third party, especially if I'm nesting iFrames, or if I only need to worry about my CSP policy.
I think what I'm getting at is this: Once I've said "allow this 3rd party site to load" in my CSP headers can that site load whatever it wants based on their CSP headers?
Thanks!
Let's say that you have site A framing site B. Site A must not set a framing policy that denies site B and site B must not set a policy that prevents being framed by A.
Site A can set "frame-src B" to explicitly allow site B to be framed. If frame-src is not set, child-src is used as a fallback, and if that is not set, default-src is used as a fallback. If none of them are restricted, all sites can be framed.
Site B can set "frame-ancestors A" to allow framing by A. This directive has no fallback. If it is not set, any site can frame site B. If it is set, only the sites listed as valid sources can frame it.
Apart for frame-src (child-src, default-src) for the framer and frame-ancestors for the framed, there is no impact on other sites by the CSP, they each control their own sources.
CSP Header directive corresponding to iframes ,
frame-src
frame-ancestors
lets say your site xyz.com and google's site "google.com".
Site xyz.com has its own csp which can controls,
Who can load xyz.com as iframe, decided by frame-ancestors directive
Who can be loaded inside 'xyz.com' as iframe, decided by frame-src directive
same scenario applies for google.com ( whose csp can decide, whom to be loaded as iframe inside its app & whom can load google.com as iframe )
Each html document has its own csp response header, which will not interfere with its host app (parent frame) or its iframes (child frames).
xyz.com 's CSP only decides whom should load it & whom it should load as frame, it cannot control its host frame or child frame ( they are considered as separate entities )
Apart from this another header X-FRAME-OPTIONS is also available with minimal control options to decide whether a site should load as frame or not.
For detailed reference :
CSP - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
X-FRAME-OPTIONS - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
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?
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.
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
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
So I'm trying to avoid using (another) page rule to disable Rocketloader for one of my subdomains, since we can't use a RegEx to select multiple specific subdomains under a single page rule, and only get 3 page rules for free accounts.
According to this page:
https://support.cloudflare.com/hc/en-us/articles/216537517-Using-Content-Security-Policy-CSP-with-Cloudflare
I can just add a header to the domain to allow scripts from CloudFlare:
add_header Content-Security-Policy "script-src 'self' ajax.cloudflare.com;";
I did so in the Nginx config for that subdomain (it's a Chronograph container actually), restarted Nginx, tested to make sure it "took", which it did:
But then when I try to load the domain, it won't load, and the inspector shows this:
Not being super familiar with this, does anyone know where I screwed it up?
where I screwed it up?
First of all, here:
I can just add a header to the domain to allow scripts from CloudFlare:
add_header Content-Security-Policy "script-src 'self' ajax.cloudflare.com;";
I did so in the Nginx config
And secondly, you trusted the report-uri service, but it failed you.
You have had an issue with ajax.cloudflare.com BEFORE adding CSP header into Nginx config (otherwise, why add it). This means that you already have a CSP published via an HTTP header or a meta tag <meta http-equiv= 'Content-Security-Policy'>.
By adding the CSP header to the Nginx configuration, you have added a second policy to the pages.
Multiple CSPs work as sequential filters - all sources must pass through both CSPs to be resolved. The second CSP allows ajax.cloudflare.com host-source, but the first one still prohibits it (that you are observe in the inspector).
You have to figure out where the first CSP is published and to add ajax.cloudflare.com into it, instead of publish second CSP.
No one know what is under the hood of the report-uri and how it will react if two CSP HTTP headers or an HTTP header paired with a meta tag are published simultaneously
Have a look which CSP and how many of them the browser actually gets, the guide is here.
In case of 2 CSP headers you will see something like that:
In case of CSP meta tag you can easily check the by inspecting the HTML code.
I think the report-uri just did not expect such a situation.
I have a web app which I want to display in an iframe in web apps with different domains. Since I have added a content-security-policy header my app refuses to display in iframe. I saw that i need to add frame-ancestors options but all the examples I see are using specific domains. How can I allow it for all domains? Is "frame ancestors *;" enough? Thanks!
Briefly - yes, * allows any sources for iframe except data:.
Pls note that frame-ancestors is not supported in the meta tag <meta http-equiv='Content-Security-Policy' content="..."> (but looks like you use HTTP header to delivery CSP, so this warn not for you).
But if you really wish to allow all frame ancestors - more reliable not specify frame-ancestors directive at all, because for now Mozilla Firefox has some bugs with it.
PS: You did not attach print screen of errors in browser console - may be iframes was block by other reason than CSP?
updated after exposed CSPs details
<html>
parent page issues CSP: default-src 'self';
since frame-src omitted, it fallback to default-src and result be: frame-src 'self'
<iframe src=''></iframe>
</html>
iframe is allowed with the same scheme://host:port as parent page loads.
'self' is tricky in that if parent loaded via HTTP:, iframe via HTTPS: will blocked in CSP2-browsers. CSP3-browsers do upgrade (see para 3) HTTP: to HTTPS:, so all OK.
If parent page issue frame-ancestors * policy, it means you allow to embed it into iframe to any another webpage.
X-Frame-Options HTTP header provide the same functionality, but it's overridden if frame-ancestor is issueed.
frame-ancestor directive does not affects <iframe> embed into page who published this CSP. It affects where it allowed to embed this page.
But <iframe> could publish its own CSP with rule frame-ancestors domain1.com domain2.com to restrict it embedding to other web-pages.
That's how it works. You could play with test of frame-ancestors to clarify details for different <iframe src=/srcdoc=.
Therefore if you embeds iframe from your own domain/subdomains, it's more safe to use:
frame-ancestors 'self';
or if you use subdomains:
frame-ancestors http://example.com https://example.com http://*.example.com https://*.example.com;
Trying to publish HTTPS content (login form) using iframe onto HTTP page.
Have permission, but do not have access to source code of HTTPS page.
Standard attempts to publish iframe do not work with this HTTPS page content.
Appears that HTTPS page x-frame-option set to DENY.
Is there any way to embed/frame/etc. this HTTPS content onto HTTP page despite x-frame objections?
This is a WordPress site. Not sure if that is relevant here.
No there is not, and this actually have nothing to do with HTTP or HTTPS, it's how the X-frame-Options header works.
When a resource returns the header of X-Frame-Options: DENY, it is not possible to show it in any iframe or iframe-like window, not even one on the same site.
You said you have permission though, so perhaps you can get the service you are using to use the ALLOW-FROM option for your service. Something like this could be configured to allow your site to frame it.
X-Frame-Options: ALLOW-FROM https://example.com/