We host a small (.Net) website for our customers; it's designed to be embedded within both our own hosted pages, and potentially within the customers own web applications (both internal and external). By far the most common way of doing this is embed it within a iframe on the site.

So our site is explicitly designed to be within a frame; however, every vulnerability and web application security scan we run complains bitterly that we haven't set the Content-Security-Policy and X-Frame headers correctly. We currently have the CSP set as

Content-Security-Policy: default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' 'self'; style-src 'unsafe-inline' 'self'; img-src 'self' data:

(Why all those unsafe? Because we use a component suite (DevExpress) that explicitly requires it.)

Even ignoring that, our security scans (via Tenable) complain that:

- 'frame-ancestors' should be set to 'none' to avoid rendering of page in <frame>, <iframe>, <object>, <embed>, or <applet>.

We're also "missing" an X-Frame-Options header:

The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page inside a frame or iframe. Sites can use this to avoid clickjacking attacks, by ensuring that their content is not embedded into other sites.

.. but the only options for this are DENY or SAMEORIGIN - we don't want either.

What CSP and X-Frame-Options should we specify for a site that is explitly designed to be embedded within another page?

EDIT: To give more context about the site in question: it's a hosted service, designed to be used by our customers for their own internal staff - it's not used, presented or designed to be available to public users (although the site is publically accessible, more for convenience for our customers to be able to access it from whereever their staff are).

More pertenantly: although the site is used to facilitate payments, it doesn't present or collect sensitive details in any way. In effect, it's just a "status" page - it shows the user the status of a transaction that's taking place in real time - it's effectively "read only" - and the only way for that transaction to progress is via a separate, and completely out-of-band process to be taking place. Without this process progressing, the page effectively just shows "Waiting ...".

  • You can use CSP frame-ancestors directive to restrict framing to specific domains. A lot of scanners are pretty basic in their understanding of these headers. They've done their job here in alerting you to the available security features, but don't worry too much if your eventual config still triggers some alerts.
    – paj28
    Nov 21, 2022 at 16:10
  • We have hundreds of customers embedding this page (a nice problem to have!) - and we don't explicitly know the domain list. We try to encourage our customers to use this site, so adding in the barrier of having to add their domains to a permitted list would be a step backwards :(
    – KenD
    Nov 21, 2022 at 16:18
  • The scanner doesn't understand detail like that. If you're happy with your page being embedded by any domain and understand and accept the risks associated with that - then just ignore the scanner.
    – paj28
    Nov 21, 2022 at 16:23
  • Just checking: do your users enter any sensitive information (credentials including OTPs, PII, payment info, non-public communications, PHI, etc.), that they specifically trust you (rather than your customers) with, into your page (not your customers' pages, your own page, the one being embedded)? Because if so, your entire security model is broken; an attacker can impersonate your embedded page and steal that info from your users who have no realistic way to know whether they're interacting with you or a phishing page.
    – CBHacking
    Nov 22, 2022 at 8:59

1 Answer 1


The usual solution here is to dynamically generate the XFO / frame-ancestors directives. Require, in the URL (probably as a query parameter), a token that identifies the page where it's to be embedded. If no such token is presented, or if the token doesn't match a known site, don't allow framing at all (or make it same-origin only, at least). This doesn't add a lot of security (none at all if you allow arbitrary domains; only a little if you have a specifically allowed list of allowed customers) but it'll make the scanners happy (well, happier; that CSP is a travesty barely better than having none at all).

Do bear in mind that a web app - that is, any page with dynamic content and user interaction - that is embedded is inherently quite risky. The extremely vast majority of users aren't going to know whether they're interacting with you or an imposter. Checking the URL bar doesn't help; they'd have to use the dev tools to check the source of the iframe, which isn't exposed to the normal browser UI anywhere at all. To approximately quote my own comment:

Do your users enter into your page (the one being embedded, NOT your customer's pages) any sensitive information - credentials including OTPs, PII, payment info, non-public communications, PHI, etc. - that they specifically trust you (rather than your customers) with? Because if so, your entire security model is broken; an attacker can impersonate your embedded page and steal that info from your users who have no realistic way to know whether they're interacting with you or a phishing page.

For a simple example, suppose you have an embeddable webapp, call it Talkr, that allows users to make comments and vote on them on arbitrary pages. Users have accounts that let them have a consistent shared identity across multiple sites, and build a trusted reputation (or filter out bad commenters). To log into their Talkr account on a given page, the user enters their email address into a login form on the embedded Talkr iframe, you send them a one-time password (OTP), and they type it into the iframe.

Suppose I, a malicious site owner, want to impersonate your users. I "embed" a fake Talkr login page into my site. It looks identical to the real one, and the user has no realistic way to know the difference. When a user enters their email address into my phishing page, I log the address and also send - from my server - that email address to your real Talkr login service. You send the user an email with an OTP, and my login phishing page changes to expect the OTP, just like the real one does. The user checks their email and enters the OTP into my phishing page. I collect the OTP and forward it to your server, again pretending to be the user. You send me back the session token that the user should have gotten. I use it to pull and display the user's data (just like the real Talkr site does); the user is none the wiser.

I can do this to as many users as visit my page and try to comment! I can now post arbitrary comments under their names, and probably also do other things like see what other sites they've commented on and vote on other comments... even on sites that aren't my own! It's basically a classic phishing attack for account takeover, with the only twists being the use of OTPs (which I added because some people think that makes them safe against phishing; it does not) and the fact that the user can't tell that the login page is fake even if they check the URL carefully because the login page is always on a third-party site.

  • Thanks - I've edited the OP with some more details about our site, and why (perhaps wrongly!) I think that it doesn't present any risk, as the site is effectively "read only" and can't be used for anything without an out-of-band (and separately authenticated) process being initiated. Please let me know your thoughts - if I'm wrong then I'd rather be wrong now than later on :)
    – KenD
    Nov 23, 2022 at 8:48
  • Based on the edit, I'm not very concerned about the phishing risk. I am curious about authentication and authorization - how does your site know what transaction to display the status of, and whether the user is authorized to view that? - but that probably has nothing to do with iframes or embedding and mostly comes down to simple web service security. Which raises an interesting point: it sounds like this embedded content could instead just be a web service, returning either JSON for populating a UI or HTML for direct injection into customer pages. That may require CORS configuration, though.
    – CBHacking
    Nov 24, 2022 at 6:37
  • We display the content more for convenience than anything - our customers can embed this page without having to do any real development. All this makes me wonder: how do card payment providers (e.g. Stripe) that provide "hosted payment pages" (which, like this, are designed to be embedded within customers own sites) solve the CSP problem? Wouldn't they have the same problem - they can't specify a CSP without knowing the domains their pages will be embedded within... or am I misunderstanding something?
    – KenD
    Nov 24, 2022 at 8:49
  • Stripe and similar do what I'm describing in the answer. You (site owner) can't use Stripe without an account - after all, Stripe needs to know where to send the money - and in that account you specify what domain(s) you'll embed in. Stripe gives you a custom URL that you use for the iframe, which tells them what site the request is (ostensibly) coming from, and they respond with a custom frame-ancestors directive allowing that site but no others. Of course, this does run into the other problem I mentioned; it'll bite them some day.
    – CBHacking
    Nov 24, 2022 at 8:54
  • Food for thought - we'll undoubtedly have to engineer something like that for ourselves in the future. Thank you!
    – KenD
    Nov 24, 2022 at 9:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .