Safeguard
Application Security•

ASVS 5.0 V3.4.6: Control Framing With CSP frame-ancestors

ASVS V3.4.6 (Level 2) requires CSP frame-ancestors on every response so pages cannot be embedded by default. X-Frame-Options alone is not enough. Config and tests.

Safeguard Security Team
3 min read

ASVS 5.0 requirement V3.4.6 says every HTTP response must use the frame-ancestors directive of the Content-Security-Policy header, so by default your pages cannot be embedded in frames, and embedding is allowed only for specific resources that need it. The older X-Frame-Options header is obsolete and cannot be relied on alone. It is a Level 2 requirement.

Why it matters

Clickjacking loads your page in a transparent frame on an attacker's site and tricks the user into clicking buttons they cannot see: approve a payment, change an email, grant an OAuth permission. The user is signed in to your site, so the action succeeds. See CWE-1021.

frame-ancestors is the standard control. It is also more expressive than X-Frame-Options, which cannot list multiple origins, and it applies to frames, iframes, objects and embeds alike.

How to implement it

Default deny:

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

Allow only your own site:

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

Allow a specific partner on one route:

Content-Security-Policy: frame-ancestors 'self' https://portal.partner.example
add_header Content-Security-Policy "frame-ancestors 'none'" always;
location /embed/ {
  add_header Content-Security-Policy "frame-ancestors 'self' https://portal.partner.example" always;
}

Careful: nginx does not inherit add_header into a location that sets its own, so repeat any other headers there. If you already ship a larger CSP, add frame-ancestors to it rather than sending two headers. It cannot be set through a meta tag, it only works as a header.

You may keep X-Frame-Options: DENY for very old browsers, but it is optional.

How to test it

curl -sI https://app.example.com/ | grep -i frame-ancestors
curl -sI https://app.example.com/settings | grep -i frame-ancestors
curl -sI https://app.example.com/missing | grep -i frame-ancestors

Then build a test page on another origin containing an iframe pointing at your site. The browser must refuse to display it and log a CSP error in the console. Repeat for each route that is meant to be embeddable and confirm only the listed parents work.

Common mistakes

Relying on X-Frame-Options. ASVS says it may not be relied upon.

Setting it only on the login page. Every response needs it, including APIs that return HTML.

Allowing a wildcard. frame-ancestors * defeats the control.

Missing on error responses. Use always.

Using a meta tag. Browsers ignore frame-ancestors there.

Quick check

Is frame-ancestors the same as frame-src?

No. frame-src limits what your page may embed. frame-ancestors limits who may embed your page.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.