Skip to main content

X-Frame-Options Clickjacking Protection Header

Issue No: 140

Category: HTTP Security Headers

Issue Type: Warning

Priority: Important


Description

Pages should declare either the X-Frame-Options HTTP response header or a Content-Security-Policy header with a frame-ancestors directive to prevent the page from being embedded in iframes on untrusted domains, blocking clickjacking attacks that trick users into performing unintended actions.


How Do We Capture It

Where to Find

HTTP response headers:

X-Frame-Options: DENY

or via Content Security Policy:

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

Extraction Flow

  1. Issue an HTTP GET to the target URL.
  2. Read the raw HTTP response headers.
  3. Extract the X-Frame-Options header value (header name is case-insensitive).
  4. Validate the value against the known directive set: DENY, SAMEORIGIN, ALLOW-FROM.
  5. Set x_frame_options_is_valid = TRUE only if the value matches a recognized directive (case-insensitive).
  6. Set x_frame_options_is_valid = NULL if X-Frame-Options header is absent.
  7. Extract the Content-Security-Policy header value.
  8. Tokenize the CSP value by splitting on semicolons.
  9. Scan the token list for a token whose name is frame-ancestors (case-insensitive).
  10. Extract the frame-ancestors directive value if found.
  11. Set has_clickjacking_protection = TRUE if has_x_frame_options = TRUE OR has_csp_frame_ancestors = TRUE.

Validation Logic

The crawler validates:

  • Whether the X-Frame-Options header is present.
  • Whether the X-Frame-Options value is a recognized directive.
  • Whether the Content-Security-Policy header is present.
  • Whether the CSP header contains a frame-ancestors directive token.
  • Whether at least one of the two mechanisms is declared.

How to Get It

Crawler should:

  1. Read HTTP response headers; extract X-Frame-Options and Content-Security-Policy headers.
  2. Validate X-Frame-Options value against known directive list; set x_frame_options_is_valid accordingly (NULL if header absent, TRUE/FALSE if present).
  3. Parse CSP header string; tokenize on ;; check for frame-ancestors token.
  4. Extract frame-ancestors directive value.
  5. Set has_clickjacking_protection = TRUE if either protection mechanism is present.

What to Store

FieldTypeComment
has_x_frame_optionsBOOLEANWhether the X-Frame-Options HTTP response header is present
x_frame_options_valueTEXTValue of the X-Frame-Options header (NULL if absent)
x_frame_options_is_validBOOLEANWhether the X-Frame-Options value is a recognized directive; NULL when the header is absent, FALSE when header is present but value is unrecognized
has_csp_headerBOOLEANWhether the Content-Security-Policy HTTP response header is present
has_csp_frame_ancestorsBOOLEANWhether the CSP header contains a frame-ancestors directive
csp_frame_ancestors_valueTEXTValue of the frame-ancestors directive (NULL if not present in CSP or if CSP header is absent)
has_clickjacking_protectionBOOLEANWhether at least one of X-Frame-Options or CSP frame-ancestors is declared

Condition for Trigger

The following trigger rules evaluate clickjacking protection presence and configuration validity.

  • Trigger Rule 1: No Clickjacking Protection Declared

    • Target Field: has_clickjacking_protection
    • Evaluation Logic: = FALSE
    • Severity: WARNING
    • Diagnostic Message: "Neither an X-Frame-Options header nor a CSP frame-ancestors directive was found. Without framing protection, this page can be embedded in an iframe on any domain, enabling clickjacking attacks that manipulate users into performing unintended actions."
  • Trigger Rule 2: X-Frame-Options Header Present With Invalid Value

    • Target Field: x_frame_options_is_valid
    • Evaluation Logic: = FALSE
    • Severity: WARNING
    • Diagnostic Message: "An X-Frame-Options header is present but its value is not a recognized directive. Browsers silently ignore unrecognized values, leaving the page without effective framing protection. Use DENY or SAMEORIGIN as the directive value."

Sources


Long Description

Clickjacking is an attack in which a malicious page embeds a target site inside a transparent iframe, overlays UI elements on top of it, and tricks users into clicking things on the legitimate site they cannot see — such as confirming a payment, granting permissions, or deleting an account. The attack requires no vulnerabilities in the target site itself; it exploits the fact that the browser will load the page inside an iframe on any domain by default.

X-Frame-Options is the original defensive HTTP header:

ValueBehavior
DENYPage cannot be embedded in any iframe regardless of origin
SAMEORIGINPage can only be embedded by pages on the same origin
ALLOW-FROM [uri]Page can only be embedded by the specified URI (deprecated — not supported in Chrome, Firefox, or Safari; silently ignored)

CSP frame-ancestors is the modern, more flexible mechanism and is supported in all current browsers. It supersedes X-Frame-Options when both are declared:

Content-Security-Policy: frame-ancestors 'none'; — no embedding allowed (equivalent to DENY)
Content-Security-Policy: frame-ancestors 'self'; — same origin only (equivalent to SAMEORIGIN)
Content-Security-Policy: frame-ancestors https://partner.example.com; — specific domain allowlist

The frame-ancestors directive takes precedence over X-Frame-Options in browsers that support CSP Level 2+. For maximum browser compatibility, declaring both headers is the safest approach.

Common failure scenarios:

1. Neither Header Declared

The page loads without restriction inside any iframe. No browser warning or block is applied. Any domain can embed the page and attempt a clickjacking attack.

2. X-Frame-Options: ALLOW-FROM (Deprecated)

ALLOW-FROM was never supported in Chrome and was removed from Firefox and Safari. Pages relying on it have no effective protection in modern browsers, even though the header appears to be set.

3. X-Frame-Options With Typo or Unknown Value

A misspelled value such as denied instead of DENY, or same-origin (hyphenated) instead of SAMEORIGIN, is silently ignored. The page is unprotected even though the header is present.

4. CSP Header Without frame-ancestors

A Content-Security-Policy header may be declared for other directives (e.g., default-src, script-src) without including frame-ancestors. The absence of frame-ancestors in the CSP provides no framing protection — this check specifically looks for the frame-ancestors token.


How to Fix

Set the X-Frame-Options header in your server or CDN configuration:

X-Frame-Options: DENY

For modern browsers, the preferred approach is to include frame-ancestors in the Content-Security-Policy header, which can be combined with other CSP directives:

Content-Security-Policy: default-src 'self'; frame-ancestors 'none';

For pages that require legitimate embedding on a partner domain:

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

Apply the strictest policy compatible with legitimate embed requirements. Pages that should never be embedded — login, password reset, payment flows, account settings — should use DENY or frame-ancestors 'none'.