Philippe De Ryck

Hi, I'm Philippe, and I help developers protect companies through better web security. As the founder of Pragmatic Web Security, I travel the world to teach practitioners the ins and outs of building secure software.

Want to learn more about OAuth 2.0 and OpenID Connect?

Save yourself days of digging through dozens of specs with this online course

More information

Current best practices to restrict framing in the browser

Frame-based attacks such as clickjacking and UI redressing may be obscure, but they are (still) very real. They even threaten APIs, which have nothing to do with iframes and web pages. This article gives an overview of the threats, discusses recent changes in framing restriction mechanisms, and provides concrete recommendations to secure modern web applications.

15 September 2020 Security Policies Browser Security, X-Frame-Options (XFO), Content Security Policy (CSP)

Best practices for framing policies

This section outlines current best practices to configure framing policies for modern frontend applications and APIs in contemporary browsers. If you want more context or need an overview of more detailed configuration options, you will want to read the full article.

Recommendation for frontend applications

Modern frontend applications typically do not rely on being framed as part of other applications. Therefore, the recommended best practice is to disable framing by sending the following headers in the response.

Current best practice for frontend applications


X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'

If necessary, you can enable framing within your application’s origin by setting the headers as follows:

Alternative recommendation for frontend applications


X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'

Recommendation for APIs

While APIs are typically not rendered in frames, allowing the rendering of responses in frames could open the API up to quirky attacks. Therefore, the recommended best practice is always to send headers to disable framing.

Current best practice for APIs


X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'

Clickjacking and UI redressing attacks

You’re still reading after you got the TL;DR version. Love it! So why does it matter that an application tells the browser not to load it in a frame?

By itself, it doesn’t really matter. The browser’s https://web.dev/same-origin-policy/” text=”Same Origin Policy” %} will prevent direct interaction between the frame and its parent if they are cross-origin. In essence, this means that a page running on https://maliciousfood.com cannot load https://restograde.com in an iframe and start inspecting its contents. Browsers have always prevented such behavior and righteously so!

Regardless of the Same Origin Policy, browsers always allow a malicious parent to frame a victim application. Additionally, the hostile parent can decide on the size, position, and styling of the frame. With these properties, the malicious parent can execute UI redressing attacks, such as clickjacking. In such an attack, the malicious page confuses the user with an ambiguous UI experience (e.g., a minuscule or invisible frame), tricking the user into interaction with the victim application in the frame.

The malicious page offers free beer to the user. What the user does not know is that the attacker overlaid the malicious page with a transparent iframe. Inside the iframe, the target application is loaded, with an action nicely lined up with the button on the malicious page. When the user clicks, the actual click will go the target application in the frame, triggering unwanted actions.
The malicious page offers free beer to the user. What the user does not know is that the attacker overlaid the malicious page with a transparent iframe. Inside the iframe, the target application is loaded, with an action nicely lined up with the button on the malicious page. When the user clicks, the actual click will go the target application in the frame, triggering unwanted actions.

Traditional UI redressing attacks result in “stolen clicks”, but more modern incarnations trick the user into dragging contents or copy/pasting potentially sensitive text. The enabler for each of these attacks is loading the victim application in a frame. If the victim can prevent that from happening, virtually all of these attacks go away.

Did you know that the name “UI Redressing” is confusing in English? Apparently “redressing” means “to rectify a situation”, which is definitely not applicable here. The naming refers to “re-dress”, as in “to dress the UI differently”. That’s also how I always interpreted it, until Eric Lawrence pointed this out on Twitter

You might be wondering if not loading the application in a frame is the best defense. Can’t the browser just detect the malicious interaction and warn the user? In theory, yes, but in practice, that turns out to be more difficult than you’d assume. This work attempted to provide a heuristics-based security mechanism to determine if UI interactions are safe or not. Unfortunately, the problem was much harder to solve than initially assumed, stalling the proposal indefinitely.

Now that we know why it matters that we restrict framing, let’s take a look at how to do that with the X-Frame-Options header and with the Content-Security-Policy header.

Framing restrictions with X-Frame-Options

The X-Frame-Options is the oldest security mechanism to restrict framing. Supporting browsers look for the X-Frame-Options header on responses that will be rendered in a frame. Based on the header’s value, the browser will either allow the response to be loaded in a frame or block the loading and return an error instead.

The X-Frame-Options header supports three configuration scenarios:

  1. Not sending an X-Frame-Options header enables the default browser behavior, which allows all framing
  2. Sending an X-Frame-Options header with a value DENY prevents all framing, regardless of the origin of the parent frame
  3. Sending an X-Frame-Options header with a value SAMEORIGIN allows framing when the parent has the same origin as the resource loaded in the frame, but denies all framing where the parent has a different origin

In the past, the header used to support a fourth configuration, with a value of ALLOW-FROM x, where x represents an origin, such as https://restograde.com. This configuration allowed framing by that particular origin, but not by any other origin. This paragraph is about the past because mainstream browsers dropped support for that option. Chrome and Webkit-based browsers never supported this feature to begin with, and Firefox recently changed its position as well. In essence, this feature is no longer useful.

Framing restrictions with Content Security Policy

More recent is Content Security Policy (CSP), an elaborate browser security policy. Like the X-Frame-Options header, a server can send a Content-Security-Policy header back to the browser. With that header, the server can tell the browser to enforce various security restrictions, including framing behavior. In this section, we will discuss the framing restriction feature of CSP in more detail. More information about CSP in general is available on MDN.

By setting the frame-ancestors directive in a CSP header, the server tells the browser who is allowed to frame the page in the response. Just like the X-Frame-Options header, the frame-ancestors directive supports four configuration scenarios:

  1. Not sending a frame-ancestors directive in the CSP header enables the default browser behavior, which allows all framing
  2. Sending a frame-ancestors 'none' directive in the CSP header prevents all framing, regardless of the origin of the parent frame
  3. Sending a frame-ancestors 'self' directive in the CSP header allows framing when the parent has the same origin as the resource loaded in the frame, but denies all framing where the parent has a different origin
  4. Sending a frame-ancestors https://restograde.com https://virtualfoodie.com directive in the CSP header allows framing by a parent from either https://restograde.com or https://virtualfoodie.com

Note that CSP’s restrictions apply not only to the direct parent of a frame, but to all ancestors of that frame.

Restricting framing in practice

Having two mechanisms to restrict framing makes it difficult to figure out which configuration makes sense. That’s why we discuss four different scenarios below, all with sample header configurations.

Deny framing by anyone

The configuration below tells the browser never to load the response in a frame.

Current best practice for APIs


X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'

This is the recommended best practice for modern frontend applications, which typically do not rely on being integrated through iframes. For applications that depend on frame-based integration, one of the other configurations will be more applicable.

Even though API responses are not intended to be rendered in frames, nothing prevents that from happening. To ensure API responses are not vulnerable to obscure attacks, it is recommended to set both framing headers on each API response as well.

Allow framing within the application’s origin

The configuration below tells the browser that a response can only be loaded in a frame if the parent is from the same origin.

Current best practice for APIs


X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'

Note that this configuration is acceptable for frontend applications that rely on framing within their origin.

Allow selective framing

The configuration below tells the browser to render the page in a frame if the parent is listed in the CSP header. In this example, framing is allowed by origins https://restograde.com and https://virtualfoodie.com.

Current best practice for APIs


Content-Security-Policy: frame-ancestors https://restograde.com https://virtualfoodie.com

This configuration should only be used in use cases that specifically depend on framing.

Note that the X-Frame-Options header no longer supports selective framing, except in Internet Explorer 10. The configuration given below allows all framing in Internet Explorer 10, which is not necessarily problematic. If IE10 users are a representative subset of your userbase, and you only need to allow a single origin, you can complement the Content-Security-Policy header with an X-Frame-Options header using the ALLOW-FROM option.

Always allow framing

To allow framing by anyone, it suffices to avoid setting either of the framing policies. Omitting the policies results in the browser allowing framing by default. This setting is not recommended, except for public applications that rely on being rendered in a frame.

Summary

To conclude, let’s summarize a long story in three concrete bullet points:

  • Both HTML-based web applications and APIs should set headers to restrict framing
  • The recommended best practice is to deny framing by setting both the X-Frame-Options header and the Content-Security-Policy header
  • Unless you are dealing with IE10 users, forget about X-Frame-Options to configure selective framing


About Dr. Philippe De Ryck

Hi, I'm Philippe, and I help developers protect companies through better web security. Learn more about my security training program, advisory services, or check out my recorded conference talks.

Want to learn more about OAuth 2.0 and OpenID Connect?

Save yourself days of digging through dozens of specs with this online course

More information
Philippe De Ryck

Dr. Philippe De Ryck

Hi, I'm Philippe, and I help developers protect companies through better web security. As the founder of Pragmatic Web Security, I travel the world to teach practitioners the ins and outs of building secure software.


Talks and workshops

You will often find me speaking and teaching at public and private events around the world. My talks always encourage developers to step up and get security right.


Articles

Security is often about small nuances. In my articles, I dive deeper into various security topics, providing concrete guidelines and advice. My articles also answer questions I often get while speaking or teaching.


Security resources

Getting security right is all about knowledge. I strongly believe in sharing that knowledge to move forward as a community. Among my resources, you can find developer cheat sheets, recorded talks, and extensive slide decks.


Mailing list

Subscribe to the Pragmatic Web Security mailing list to stay up to date on the latest activities and resources.

Subscribe