A little break today from the usual Apricot fare.
I recently learned about the security risk of click-jacking, which is a way to embed somebody else’s web content under a transparent overlay to trick users into doing things they shouldn’t. Somebody could embed the content on this blog, for example, on another website with transparent links overlaid that download malicious code.
To be honest, I don’t know how much of a risk this really is, but the 80% solution is super easy (in theory) and doesn’t seem like it can hurt.
A solution, in theory
One way to prevent your web content from being embedded on another site in a frame specifically is to include the X-Frame-Options
header in the HTTP response, with the policy set to DENY
or SAMEORIGIN
. I prefer DENY
because I don’t think I’ll ever need to embed my own blog content, but it seems like SAMEORIGIN
is more common.
OK, simple enough, but how to make this happen?
A fly in the ointment
This blog is built with the Quarto framework and published on Netlify.
Let’s start with Netlify, because that’s there the HTTP response header is ultimately going to be served from. Netlify’s Custom Headers page says we just need to include a file _headers
in our publish directory and even gives an example for the clickjacking problem:
/*
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
OK, now back to Quarto, where we find the fly in the ointment.1 We need to get this file into our publish directory (by default this is called _site
). But we can’t just put the file into the _site
directory because Quarto deletes it every time it renders the source.
The Quarto docs do have a page about headers, but they’re HTML headers, not HTTP, so that’s no help.
The solution, for real
Instead, we need to refer to Quarto’s site resources page, which shows how to include files like _headers
(or other things like images) in the _site
directory after rendering.
In the root directory of our project we have a config file called _quarto.yml
. In the project
field of that file we list _headers
as a resource, which tells Quarto to copy the _headers
file over every time it renders.
project:
type: website
resources:
- "_headers"
So just leave the _headers
file in the root directory. Render the changes and publish to Netlify and we’re good to go.
$ quarto render
$ quarto publish --no-render netlify
To verify, open the dev tools in your favorite browser and go to the network tab. Open (or refresh) your blog and click on the top entry, which should be the page itself (not a secondary resource). To the right, you should see a section for Response Headers, and you should see the new X-Frame-Options
entry.
Footnotes
I love Quarto and can’t recommend it enough for technical writing of almost any kind, but for web and blog applications the documentation can be a bit thin.↩︎