JB

X-Frame-Options: How to prevent drag-and-drop style clickjacking attacks

ClickjackingX-Frame-OptionsWeb SecurityCyber ThreatsHTTP Headers
Apr 11, 2024

Shielding digital apps against sneaky moves is as important as developing them. Clickjacking attacks, especially through drag-and-drop actions, represent a clever trick in the hacker's playbook. These attacks can quietly compromise user interaction, turning innocent clicks into unintended actions. That's where the X-Frame-Options header comes into play, a straightforward but powerful one-line feature in our web security toolkit.

TLDR - Shielding from Clickjacking Attacks

1. Is your server NOT returning HTML?

Then you don't need to use the X-Frame-Options header.

2. Is your server returning HTML?

So you must use the X-Frame-Options header to prevent clickjacking attacks.

2.1 Don't you need to embed your page in another?

Configure X-Frame-Options as `DENY` value.

2.2 Do you need to embed your page in another?

So you can't use the `DENY` value, you have to choose between `SAMEORIGIN` and `ALLOW-FROM https://example.com`.

2.2.1 Only in the same origin?

Configure X-Frame-Options as `SAMEORIGIN`.

2.2.2 In any origin?

Configure X-Frame-Options as `ALLOW-FROM https://example.com`.

3. Is your server returning raw sensitive information (like name, phone number, email and physical addresses, search history, preferences, partial credit card numbers, and more) even in JSON, XML or another data structure in a public endpoint with over permissive cors setting and without authentication?

For precaution use the X-Frame-Options header as `DENY` value and revaluate if you really need a public endpoint without authentication returning sensitive information, probably it's not necessary.

4. Is your company trying to beat a security benchmark or get a security certification?

Use the X-Frame-Options header with `DENY` or any suitable value to your case according the previous recommendations.

When do the X-Frame-Options header or clickjacking attacks matter?

Diagram illustrating the architecture of a clickjacking attack, highlighting how malicious iframes overlay legitimate content

Only when you have a server that is returning HTML content.

If you have an API/server that is not returning HTML content, then the X-Frame-Options header is mostly not relevant. For example, if your server is returning JSON, XML, or any other non-HTML content, then the X-Frame-Options header is pointless. This header is intended to be used with HTML responses, and as such may provide little or no security benefits on an API that does not return HTML.

What can be the little security benefits for servers that do not return any HTM? If your company is trying to beat a security benchmark or get a security certification, maybe the tools used to verify the security of your application will check if the X-Frame-Options header is present in the response. If it is not present, the tool may consider it a security vulnerability even not returning HTML.

So we have to use our critical thinking here, when you are not returning HTML, mostly you don't have any security issues, but when you are returning HTML, you should use the X-Frame-Options header to prevent clickjacking attacks because you have a security issue.

Who can face clickjacking attacks?

Anyone can face it. Imagine you're logging into your online banking website to check your account balance or make a transaction. Now, envision a scenario where, unbeknownst to you, a hacker has overlaid an invisible frame over the entire webpage. This frame is a part of a malicious website controlled by the hacker, but you can't see it because it's transparent.

Visual example of a drag-and-drop scenario vulnerable to clickjacking, showing both the user's intended action and the hacker's overlay.

In a drag-and-drop clickjacking attack, you might think you're dragging a file, like a PDF of your bank statement or any other file with sensitive data, to upload it to your trustable website. However, because of the invisible layer, you're actually dragging and dropping your file into a hidden area set up by the attacker. As a result, you might inadvertently upload sensitive information, such as personal financial details, directly to the hacker's site without realizing it.

This situation is akin to using an ATM where a fraudster has cleverly placed a fake keypad overlay on the real one. You think you're entering your PIN on the bank's machine, but you're actually entering it on the fraudster's device.

What are the ways that attackers can use clickjacking attacks?

They will need 3 steps to perform a clickjacking attack:

1. Creating a malicious website and Layering with an Iframe: The core technique involves using an `<iframe>` HTML element, which can display content from another website within the current page. The attacker places this iframe on their malicious webpage but points it to a legitimate site that the victim trusts, like a financial institution or social media platform.

2. Making the Iframe Invisible: To hide their trickery, the hacker sets the iframe to be transparent or so small and positioned under a seemingly harmless element on the page (like a button, input or image) that the victim intends to interact with. CSS (Cascading Style Sheets) and JavaScript are used to manipulate the appearance and behavior of the iframe, making the overlay invisible or deceptive.

3. Tricking the User: The attacker then lures the victim to the malicious webpage through phishing emails, social media messages, or other means. The message might promise a reward, require action for account security, or invoke urgency to compel the user to visit the site. Once on the malicious webpage, the victim is tricked into performing an action like clicking a button, dragging, and dropping a file, or other gestures. Because of the transparent iframe overlaying the legitimate content, these actions are redirected to the attacker's site or cause unintended interactions with the legitimate site but under the attacker's control.

Why would attackers do this?

It's a lazy attack because the attacker does not need to do something like cloning all the website using HTML and CSS, so it's easier to do it. Depending on the nature of the overlay and the intended action, this could result in sensitive information being sent to the attacker, account settings being changed without the user's knowledge, or other malicious outcomes.

How can we prevent it?

As this attack does not depend solely and exclusively on a technical breach, but on the user interacting with a fake page that is using content from a real one. Two fronts can be worked on, the first would be to guide users to always check the domain they are accessing and the second would be to insert the "X-Frame-Options" header with the appropriate value in your application.

Where should the developer/Ops/Security team configure the X-Frame-Options

Depending on the tool you are using, the way you can configure the `X-frame-Options header` will vary, I will leave 3 different examples here, one at the proxy level, another at the application level, and the final at the infra level:

1. Nginx add header sample:

```nginx
add_header X-Frame-Options "SAMEORIGIN" always;
```

2. Next.js sample:

File: next.config.js

```js
module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)?', // Matches all pages
        headers: [
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
        ],
      },
    ];
  },
};
```

3. Cloudfrount sample:

Resource: AWS::CloudFront::ResponseHeadersPolicy

Pattern value supported: ^(DENY|SAMEORIGIN)$

```
{
  "FrameOption" : "DENY",
  "Override" : true
}
```

If the tool/framework/infra/proxy that you're using is not one of above, you can search for the documentation of the tool you are using and look for the `X-Frame-Options` header configuration.

How to test it?

It's quite simple, you can just use the following curl command to check the response headers:

```bash
>  curl -s -D - -o /dev/null https://www.google.com
HTTP/2 200
date: Fri, 18 Oct 2024 16:04:56 GMT
expires: -1
cache-control: private, max-age=0
content-type: text/html; charset=ISO-8859-1
content-security-policy-report-only: object-src 'none';base-uri 'self';script-src 'nonce-zkQ3tflPjOHWCgaqZwzgNg' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
accept-ch: Sec-CH-Prefers-Color-Scheme
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
server: gws
x-xss-protection: 0
x-frame-options: SAMEORIGIN
set-cookie: AEC=AVYB7cooUo_HU2odhD7env9xfg0V9yVsumNBG3l36icPdz2hiUAnBmJM7Q; expires=Wed, 16-Apr-2025 16:04:56 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax
set-cookie: NID=518=HLbu7preCnjcqhJDQkXlBBDlmSAt8JDKJFXUz6sg66lnP20hCJ45QBX6DxSadNllDX1KgpEY01s0Cj6qYY3Gy8NtEg-bqLpxd1uuhTeqCeuVo7GZgmncY81Z-UK9n7Nsc4MxvkllgOuuUb149cIcb117ELIbaDm4_ijRo2xiOCvyASmB8JgIJmpzpr48eSx252Q; expires=Sat, 19-Apr-2025 16:04:56 GMT; path=/; domain=.google.com; HttpOnly
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
accept-ranges: none
vary: Accept-Encoding
```

The options used:

  • -s: To avoid showing the progress bar.
  • -D -: Dump headers to stdout.
  • -o /dev/null: Ignore response body

In the sample above you can see the x-frame-options header as SAMEORIGIN.

Extra - Gotcha/Captcha attack

This kind of phishing can reach a whole new level, using X-Frame-Options breach into a different attack than clickjacking, especially when you have a "GET" method in a public endpoint returning sensitive or partial sensitive information with over permissive cors setting and without authentication.

The fake Captcha attack also known as a Gotcha attack can use several Iframe's to render in a scrambled way part of the information that its endpoint is returned and as a double factor fake, the malicious page can ask the user to fill in the rest of the information and thus steal private user data.

Imagine you have an API/server that returns part of the user's physical address like "Manhattan, NY 10XXXXX, United States", and using a bunch of iframe like the below one, the malicious attacker can render this in a captcha way and ask the user to fill in the rest of the information like the street, number, and zip code.

```html
<iframe src=”//[some-hostname].com/api/reveal-address/”></iframe>
```

So when the user completes the information like "Manhattan, NY 10036123, United States" (I typed a random address) the malicious page can steal the user's address and use it to perform a social engineering attack.

Again, this kind of attack depends on the user interacting with a fake page that is using content from a real one, so the same prevention measures can be used here, guide users to always check the domain they are accessing and insert the "X-Frame-Options" header with the appropriate value in your application, but in this special case, I rather prefer to do not have any public GET endpoint returning sensitive or partial sensitive information.

In the end, similar to how the Content-Type header indicates the type of response from a request so your fetcher can parse, the X-Frame-Options header determines how the browser should behave when someone tries to embed the content in an iframe from any domain.

So now that you know how to handle this sneaky move, watch your system and shield your apps.

Sapere Aude.

References

  • Helme, Scott. Hardening your HTTP response headers, 2015. Available at: https://scotthelme.co.uk/hardening-your-http-response-headers/. Accessed on February 06, 2024.
  • OWASP. OWASP Secure Headers Project, 2019. Available at: https://owasp.org/www-project-secure-headers/#div-headers. Accessed on February 06, 2024.
  • De Ceukelaire, Inti. GOTCHA: Taking phishing to a whole new level, 2019. Available at: https://medium.com/intigriti/gotcha-taking-phishing-to-a-whole-new-level-72eda9e30bef. Accessed on February 09, 2024.
  • Herzberg, Amir. Tell Me About Yourself: The Malicious CAPTCHA Attack, 2016. Available at: https://www.researchgate.net/publication/288280540_Tell_Me_About_Yourself_The_Malicious_CAPTCHA_Attack. Accessed on February 09, 2024.