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
- OWASP ASVS 5.0, requirement V3.4.6
- MDN: CSP frame-ancestors