I don’t use frames. So I’m safe, right?
With our focus on cranking out functionality, it’s easy to overlook the security implication of frames, especially if we’re building an application that doesn’t use frames. However, clickjacking is a serious security threat against any application regardless of its use of frames and really should be top of mind in any security review.
In a clickjacking attack, also known as UI redress, an attacker creates a malicious site to which he lures victims, perhaps with promises of free prizes. The attacker uses clever CSS with overlays to frame your page within the malicious site such that the victim does not realize that your site has been included within a frame. By clicking on a button for a prize, the victim unintentionally authorizes a transaction on your site, enabling fund transfers or fraudulent purchases. If the user is logged into your site at this time, the browser will send all of the cookies associated with your site along with the fraudulent request, including any session identifier, fooling your application into believing that this is a valid user request.
Oh, so you’re talking about CSRF?
If this sounds like Cross Site Request Forgery (CSRF), that is because the attacks do share certain traits. In both CSRF and clickjacking, the attacker takes advantage of a victim visiting the attacker’s malicious site while simultaneously logged into your site. The attacker exploits the browser behaviour of sending cookies to all requests to a particular domain regardless of whether the requesting page belongs to that domain. However, the typical CSRF defense of adding a random token to a hidden field of an HTML form will not work against clickjacking because in clickjacking it is your form that has been framed, including the anti-CSRF token.
How did we get into this mess?
Frames emerged like many aspects of the web–without standardization and without a thorough consideration of security implications. Netscape Navigator introduced frames in version 2.0 in March 1996. In a race to offer the most features, other browser vendors soon followed. At the time, Netscape proposed frames to the W3C for inclusion in the HTML 3.0 standard, but frames did not make it into HTML formally until 4.0 in December 1997.
There are two ways of using frames in HTML. Using a frameset tag, you can compose a web page entirely of frames. Or using an iframe tag, you can insert a frame anywhere within a page. Each frame can display a distinct page that can originate from any domain. A lot of ugly sites were created using framesets in the late 90s. Eventually, there were complaints that framesets hurt usability and accessibility, and in October 2014 the W3C obsoleted the frameset tag with the publication of the HTML5 standard. Of course, all major browsers still support the frameset tag for backward compatibility. And iframes, still in the HTML5 standard, are quite common on the web.
Early browser implementations of frames were not so dangerous. With big ugly borders, it was easy to distinguish one frame from another. But with the evolution of rich styling, the problem grew worse. Jesse Ruderman pointed this out in a bug report to Mozilla in 2002, but was ignored. It was not until 2008 when Robert Hansen and Jeremiah Grossman revealed a particularly disturbing exploit that browser vendors took notice.
Good grief. So how did this get fixed?
Among browser vendors, Microsoft was first to the rescue. In 2009, it introduced the X-Frame-Options HTTP header with Internet Explorer 8. Although there were already discussions under way regarding Content Security Policy, a more general solution that I’ve discussed in an earlier post, the standard had yet to be agreed upon, and the Microsoft IE team felt the problem needed an urgent fix. Other browser vendors followed, but standardization came only much later. It was not until 2013 that the IETF issued an informational standard documenting usage of the X-Frame-Options header.
OK, so how does this X-Frame-Options header work?
The X-Frame-Options (XFO) header takes one of three values: DENY, SAMEORIGIN, or ALLOW-FROM. DENY forbids the display of the page within a frame in all cases, clearly the most secure option. SAMEORIGIN allows the page to be framed but only by pages with the same origin, which is less secure because of differences in implementations. The ALLOW-FROM value is used to together with a specific domain, meaning that pages from the specified domain can frame the page. The ALLOW-FROM value supports only a single domain. Anything following the host name, that is after the slash, will be ignored. Obviously, this is the least secure XFO value, especially if the external domain is not completely trustworthy.
Unfortunately, there has been confusion and browser inconsistencies around multiple levels of embedding, that is when one page embeds another that embeds another and so on. Should the ALLOW-FROM domain match the embedding page or the initial page loaded by the browser or every embedding page along the chain.
So is this X-Frame-Options the end of the story?
The CSP standard includes directives for preventing a page from loading untrustworthy content as well as being loaded within an untrusted page.
The first version of CSP included a directive named frame-src. Version two deprecated this header in favor of child-src. Both directives limit content loaded into frames to a list of specified domains. Use both directives until all browsers support the newer standard.
More relevant to this discussion is the frame-ancestors directive. It defines which domains can embed the protected page within content-embedding HTML tags: frame, iframe, object, embed, and applet. Setting the directive to none is similar to setting X-Frame-Options: Deny. However, the CSP specification is clear on multiple levels of embedding. As its name implies, the frame-ancestors directive requires that every embedding page in the chain matches one of the domains specified in the directive.
What else might I do to prevent clickjacking?
Other than HTTP security headers, there are other common sense defenses relevant to both clickjacking and CSRF. Given that each attack works only when a victim visits an attacker’s site while logged into your site, try to limit the chances for this unfortunate coincidence. Time out sessions after a reasonable interval. Make it easy for users to log off. And require users to re-authenticate prior to transactions.
Which option should I choose?
|Guidance on configuring web servers||https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options|
|IETF Informational RFC on X-Frame-Options||https://tools.ietf.org/html/rfc7034|
|Other benefits of X-Frame-Options||https://frederik-braun.com/xfo-clickjacking.pdf|
|OWASP Clickjacking Defense Cheat Sheet||https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet|