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
- Issue an HTTP GET to the target URL.
- Read the raw HTTP response headers.
- Extract the
X-Frame-Optionsheader value (header name is case-insensitive). - Validate the value against the known directive set:
DENY,SAMEORIGIN,ALLOW-FROM. - Set
x_frame_options_is_valid = TRUEonly if the value matches a recognized directive (case-insensitive). - Set
x_frame_options_is_valid = NULLifX-Frame-Optionsheader is absent. - Extract the
Content-Security-Policyheader value. - Tokenize the CSP value by splitting on semicolons.
- Scan the token list for a token whose name is
frame-ancestors(case-insensitive). - Extract the
frame-ancestorsdirective value if found. - Set
has_clickjacking_protection = TRUEifhas_x_frame_options = TRUEORhas_csp_frame_ancestors = TRUE.
Validation Logic
The crawler validates:
- Whether the
X-Frame-Optionsheader is present. - Whether the
X-Frame-Optionsvalue is a recognized directive. - Whether the
Content-Security-Policyheader is present. - Whether the CSP header contains a
frame-ancestorsdirective token. - Whether at least one of the two mechanisms is declared.
How to Get It
Crawler should:
- Read HTTP response headers; extract
X-Frame-OptionsandContent-Security-Policyheaders. - Validate
X-Frame-Optionsvalue against known directive list; setx_frame_options_is_validaccordingly (NULL if header absent, TRUE/FALSE if present). - Parse CSP header string; tokenize on
;; check forframe-ancestorstoken. - Extract
frame-ancestorsdirective value. - Set
has_clickjacking_protection = TRUEif either protection mechanism is present.
What to Store
| Field | Type | Comment |
|---|---|---|
has_x_frame_options | BOOLEAN | Whether the X-Frame-Options HTTP response header is present |
x_frame_options_value | TEXT | Value of the X-Frame-Options header (NULL if absent) |
x_frame_options_is_valid | BOOLEAN | Whether 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_header | BOOLEAN | Whether the Content-Security-Policy HTTP response header is present |
has_csp_frame_ancestors | BOOLEAN | Whether the CSP header contains a frame-ancestors directive |
csp_frame_ancestors_value | TEXT | Value of the frame-ancestors directive (NULL if not present in CSP or if CSP header is absent) |
has_clickjacking_protection | BOOLEAN | Whether 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."
- Target Field:
-
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."
- Target Field:
Sources
- MDN — X-Frame-Options
- MDN — CSP: frame-ancestors
- OWASP — Clickjacking Defense Cheat Sheet
- RFC 7034 — HTTP Header Field X-Frame-Options
- W3C — Content Security Policy Level 3: frame-ancestors
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:
| Value | Behavior |
|---|---|
DENY | Page cannot be embedded in any iframe regardless of origin |
SAMEORIGIN | Page 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'.