Monday, May 9, 2016

Embedding Redmine within an IFrame

In this post I'm going to describe how I used NGINX to remove the X-Frame-Options so that we could embed Redmine inside another webapp.  

Our dev team at Indosoft, Inc had created a set of Work Flow tools to help process tickets in our issue tracking software.  The workflow tools run on one server and they embed the Redmine webapp inside an IFrame. Redmine is running in an LXC container and is proxied by NGINX.  The NGINX container forces all the clients to use a HTTPS connection.  The workflow tool is also using a valid SSL certificate.

The problem is that Redmine, which is written in Ruby, was setting the X-Frame-Options header in the HTTP response to 'X-Frame-Options SAMEORIGIN'.   Since the workflow tools and Redmine had different URL's, the web browsers where refusing to load Redmine in the IFrame.

I knew that I should be able to use NGINX to solve this problem.  It turns out that the 'ALLOW-FROM uri' option should have been able to solve the problem, however it is not and will not be implemented in Chrome.  If you want to read that conversation that the Chrome Dev's had, follow this link:  https://bugs.webkit.org/show_bug.cgi?id=94836

So it seems like the easiest and most straight forward way is to remove the 'X-Frame-Options' is the response header as it passes through NGINX.  NGINX's proxy module gives us the tool we need; using the proxy_hide_header option we can strip any header out of the response from the proxied server before sending it off to the client.

        location / {
                proxy_pass http://x.x.x.x:80/;
                proxy_set_header Host            $host;
                proxy_set_header X-Forwarded-For $remote_addr;
                proxy_hide_header X-Frame-Options ;
        }
This approach is at best only a partial fix.  This is weakens the security of our embedded webapp as the users could be left vulnerable to clickjacking and XSS exploits. We have a few options to explore if this is unacceptable.  NGINX could be configured to only strip this header when the client IP is on a white list with a bit of system administration overhead but again, that is not ideal.  The proper solution to improve the security of this setup seems to be to implement Content Security Policy (CSP).

In my follow up article to this one,  I'll tackle how to inject the CSP directives into the Response Headers.

No comments: