I have an HTML page that is designed to be iframed into a different website.
With a CSP frame-ancestors directive I can restrict in which pages it is allowed to be iframed.
This disallows iframing into the wrong site which is the intent of this security policy.
In my scenario I also want to block loading this page stand alone because it does not make sense and will confuse people trying to interact with it. It only should be used in the iframed context.
As far as I can tell CSP does not have an option to block a page from being run standalone. It works as a whitelist so I can only allow more iframes and not deny something.
So far I have only found client-side solutions (like this which uses JavaScript) to detect this in the browser and then do something. Although this is a viable solution I prefer already blocking this at the server end.
I have had a look at the request headers and found that only on the first request the "Referer:" seems to be ‘usable’ but not reliable.
So my question is if it is possible to detect/block this serverside or is there a way to make the browser block this (similar to a CSP)? Or is the JavaScript route really the only current way to handle this?
2
Answers
I have found a way to do this that works with only modern browsers (good enough for me) and works in my specific context.
Why and how this works for my context:
The problem is that if AFTER you have done this (i.e. there is a cookie with an active session) and then open the iframed url in a separate window utside of the iframed situation; it will work (which I do not want).
While trying things out I noticed a message in the browser console
Third-party cookie will be blocked in future Chrome versions as part of Privacy Sandbox.
.Third-party cookie will be blocked in future Microsoft Edge versions as part of unpartitioned third-party cookie deprecation.
So I did some digging about this partitioning and it turns out that if a cookie is partitioned then it will have a separate scope between inside and outside the iframe.
So if the page in the iframe received the parameters that make it valid, then the same page opened outside the iframe will be in a different session! A different session which is missing the required attributes and thus I can cleanly show a serverside error.
A skilled user can easily circumvent this by changing their cookies. For the usecase I have this is enough because it makes this happening by accident impossible.
After some further digging I found that enabling this is quite easy (once you know how to do it):
Based upon this https://github.com/spring-projects/spring-framework/issues/31454#issuecomment-1905442337 I now have this in my (kotlin) code and I have what I wanted.
If
then your page is not inside an
iframe
. Yet, this is only reliable if you have the same origin. Since your scenario is all about cases when the origin is not the same, you will need a more neat solution. A way to solve this is to make sure that your page will only be displayed if it receives a message.Steps to implement this:
1. Make sure that the page does not load your actual structure upon GET
Make sure that your page is having a proper
head
, withlink
andscript
andmeta
tags and an emptybody
.2. Make sure your script embedded into the iframe has a postMessage handler
Read about
postMessage
here. Example from that page:Make sure that inside the
data
you pass something that validates the page being displayed, like a token that your server understands.3. Send a request from the page upon receiving a token to the server
So, you receive some
data
, let’s make sure that you send it to the server and, if valid, then receive the contents that you wanted to display and append them tobody
asinnerHTML
. A simplistic solution would be at your server to generate a one-time alphanumeric token (maybe a random text) that is linked to the host of the page where your iframe is to be embedded and once your server receives this request, check whether the token exists and it is linked to the exact host that has sent you the host, respond to the request with valid content (if the token was valid) or an error message (if the token was invalid) and of course, remove the token after validating, but before responding.4. Script for the parent page to embed
Implement a Javascript script that
postMessage
and pass valid data, including the token)