April 30, 2015

April 10, 2015

Please reload

Recent Posts

I'm busy working on my blog posts. Watch this space!

Please reload

Featured Posts

Working with X-Frame-Options and CSP frame-ancestors

May 18, 2018

Have you heard of the Content Security Policy (CSP) “frame-ancestors” directive? It is a newer alternative to the X-Frame-Options header which offers better control and broad but not universal browser support. First a bit of history.


The directive was originally proposed in the February 2014 CSP working draft. The earliest browser support by Firefox and Opera came at the end the year, followed by Chrome in early 2015. Safari and Edge implemented the directive in mid/late 2016. Perhaps it is no surprise, given Internet Explorer’s legacy status, that frame-ancestors support has not been added, nor is it expected.


On the other hand, the X-Frame-Options header is supported in the current version of every browser listed above, including Internet Explorer. Well, almost. Chrome does not support the ALLOW-FROM directive in X-Frame-Options. If we are going to do anything involving other domains, we need something similar. Let us see if we can stitch together a patchwork configuration involving both headers which does something more than just allow same-origin framing.


Getting more specific, suppose our requirement is to configure the server in such a way that our pages may be framed from the site itself as well as exactly one other site. And there should be a way to allow other sites in the future. As for CSP, the solution is quite simple. We just use this header:


Content-Security-Policy: frame-ancestors 'self'                     https://www.example.com;                                          


This one line shows the advantage of the newer frame-ancestors directive. If it is not immediately clear why, let us proceed to the comparable configuration for X-Frame-Options so that we cover the IE case as well.


We of course have both the ALLOW-FROM and SAMEORIGIN directives with X-Frame-Options, and that would appear to be all we need, but for reasons that are unclear, we cannot use them both at the same time. If we are going to allow framing, we must choose exactly one site or allow framing by all sites. We cannot allow our own site and another one, but no other sites.


But all is not lost. We can achieve the desired result using only web server directives and a linking convention. We will require that when https://example.com/ wants to frame our content, they must use a special subdomain to do so, e.g. if they want to frame page.html, then they must use the URL https://example.ourdomain.com/page.html.


Then we create a server configuration for example.ourdomain.com which includes this header:


X-Frame-Options: ALLOW-FROM https://www.example.com/                    


And of course the server for www.ourdomain.com will have a different header, because the site is allowed to frame itself:


X-Frame-Options: SAMEORIGIN                                             


Note that we are not actually duplicating the site or application here. We are instructing the same web server to add different headers depending on what hostname was supplied in the URL. On Nginx this can be done using multiple server blocks for example.


We now have separate solutions that we need to combine together. But that is not difficult. The single-line CSP header must be split into two different variants, one for each of the server configurations we are using. Bringing everything together, the final headers are below.


Headers for www.ourdomain.com config:


Content-Security-Policy: frame-ancestors 'self';                        
: SAMEORIGIN                                             


Headers for example.ourdomain.com config:


Content-Security-Policy: frame-ancestors https://www.example.com;       
X-Frame-Options: ALLOW-FROM https://www.example.com/                    


The configuration should work in theory. But what happens in practice if the browser sees both the CSP header and the X-Frame-Options header? We tested all of the browsers discussed previously, and they all behave as expected. When same-origin is specified in the configuration, only framing from the same origin is allowed. When other-origin is specified, only framing from that other origin is allowed. Note that we did not test second or third order framing, where the content contains frames inside other frames.


It wasn't really so complicated after all. State the intended behavior twice, once with each header. Perhaps it all could have been said better in fewer words. But until Microsoft stops shipping IE with Windows, the X-Frame-Options header still has its uses. And even when CSP frame-ancestors completely takes over, there still may be a need to separate framing contexts using different subdomains (or perhaps URL parameters), so this sort of split-domain configuration may outlive even the old header.


Share on Facebook
Share on Twitter
Please reload

Follow Us

I'm busy working on my blog posts. Watch this space!

Please reload

Search By Tags