I'm so lost, or at least, struggling. Why is modern HTML/CSS like this?
So there's apparently a hidden <checkbox>, and then a <label> "for" the checkbox that contains no text, but takes up space due to CSS properties. And also apparently clicking on the label toggles the checkbox because, it just works that way by default? And then the CSS properties can vary depending on the checkbox state, without JavaScript, because that's just built into CSS for some reason? And then in the second box, it's using another label for the same checkbox, so it shares that state.
Then the actual SVG... just defines filters, and doesn't actually draw anything. But the various demos get to pull filter definitions out of the SVG?
And two separate <feTile> tags define a filter in conjunction, one describing the region to take as a tile and the second describing where to tile it? Whereas all the other filters are just transforms on a common region? Why is it like that (as opposed to, say, having separate source and destination coordinates in the attributes for a single <feTile> tag)?
And what even are these <fake-frame> and <art-frame> elements?
I think it's pretty neat. It allows me to build cool interactive stuff such as the post in question without having to use JavaScript.
> And also apparently clicking on the label toggles the checkbox because, it just works that way by default?
Yes, that's how semantic HTML forms work.
> And then the CSS properties can vary depending on the checkbox state, without JavaScript, because that's just built into CSS for some reason?
Yes, it makes sense to be able to style an unchecked checkbox differently from a checked one. And I'm just using CSS's `:has()` to check for the state: html:has(#foo:checked) label[for=foo] { ... }
> And two separate <feTile> tags define a filter in conjunction, one describing the region to take as a tile and the second describing where to tile it?
<feTile> is a single element filter just like all the other ones. It just tiles the current image to the desired size. If the input is bigger than the output, it functions as a crop instead. So I use two of them to achieve a crop + tile.
> And what even are these <fake-frame> and <art-frame> elements?
They're autonomous custom elements, you can just make them up instead of using div-soup.
I touched on it in this post: https://lyra.horse/blog/2025/08/you-dont-need-js/
Aha.
> I touched on it in this post: https://lyra.horse/blog/2025/08/you-dont-need-js/
I coincidentally was given that link elsewhere since posting and have been reading it and clarified much of the rest as well. Amazing work on the blog overall.
Most of the things you mention are not "modern"
> And also apparently clicking on the label toggles the checkbox because, it just works that way by default?
This goes back to the 90s. Clicking on a form widget label causes the widget to be focused.
I believe the original rationale is that is how desktop UIs do it. Also for checkboxes and radio buttons the hitbox would otherwise be quite small.
> And then the CSS properties can vary depending on the checkbox state, without JavaScript, because that's just built into CSS for some reason?
Well yes, if you want to customize the way checkboxes look you need to apply different styles depending on their state. Support for this literally goes back to version 1 of firefox.
> But the various demos get to pull filter definitions out of the SVG?
That's kind of a natural consequence of being able to embed SVG namespace elements directly in html. CSS supports it via the filter property, but i think even before that property existed you could probably do it via direct embedding svg in html or vice versa.
Anyways, my point is this isn't a situation of, what has modern html wrought. Most of this is very old features. I bet you probably could have done the same attack a decade ago.
It doesn't surprise me that this is possible for the checkbox, but it does surprise me that the label responds to the corresponding checkbox's state. (I take it that the styling is being applied to the labels, simply so that multiple labels can share state by all being "for" the same hidden checkbox.)
> That's kind of a natural consequence of being able to embed SVG namespace elements directly in html. CSS supports it via the filter property, but i think even before that property existed you could probably do it via direct embedding svg in html or vice versa.
I've only ever used SVG for... scalable vector graphics. I don't understand why CSS needs access. I get that SVG uses tags so that individual elements of the drawing can be in the DOM and then e.g. get animated by JavaScript. But I would have expected that to require JavaScript.
I dont really think css filter is neccesary here though. I suspect the exploit could be implemented without that part just by embedding svg on the page.
The base form of this attack goes back to the original CSS 1.
Honestly you are massively overreacting. This type of attack was much much easier to pull off in the late 2000s then it is now. Its basically impossible in practise now a days.
Turning off JS permanently is like keeping your wallet in a safe you carry around all the time because once in a while you need to visit the dangerous parts of the town.
Most site shouldn't run any js after content is loaded.
I hope there's something like <body onload="js.disable()">
I can only do it manually in DevTool.
As a web developer: You can use Content Security Policy to limit or disable JS, as well as other resources such as CSS and images.
JS is essential for polished UX when you have highly interactive components. Technically mapquest got server-rendered interactive maps working, but no one would choose that over the usability of Google Maps or OpenStreetMaps today
apparently, single-page-apps is an unstoppable trend. I tried to disable JS and 99% site won't work.
But for content sites, after the article is loaded, disabling JS provides a much better reading experience.
> but no one would choose that over the usability of Google Maps or OpenStreetMaps today
That's a valid use for JS. But if you think about it, can we make a js free map tool using technics from OP's article? https://codepen.io/rebane2001/details/OPVQXMv
https://stackoverflow.com/questions/4908893/what-logic-gates...
You can do that by either adding a header to your network requests, o̶r̶ ̶b̶y̶ ̶a̶d̶d̶i̶n̶g̶ ̶t̶h̶e̶ ̶f̶o̶l̶l̶o̶w̶i̶n̶g̶ ̶m̶e̶t̶a̶ ̶t̶a̶g̶ ̶t̶o̶ ̶y̶o̶u̶r̶ ̶p̶a̶g̶e̶:̶
̶<̶m̶e̶t̶a̶ ̶h̶t̶t̶p̶-̶e̶q̶u̶i̶v̶=̶"̶X̶-̶F̶r̶a̶m̶e̶-̶O̶p̶t̶i̶o̶n̶s̶"̶ ̶c̶o̶n̶t̶e̶n̶t̶=̶"̶D̶E̶N̶Y̶"̶>̶
EDIT:
According to MDN, it will only work by adding it to your headers. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/...
Basically i dont think anyone should worry about this.
All website operators should read this imo: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_...
Everything is a target. You can’t assume safety based on reputation or ubiquitousness.
There are so many examples of the trusted well-used thing failing security to mention.
I've been able to make realistic attacks against multiple targets. Many services, such as Google Docs, need to enable cross-origin framing for their functionality.
And beyond that, even if you restrict the framing, it might still be possible to clickjack as a part of a more complex attack chain, see: https://lyra.horse/blog/2024/09/using-youtube-to-steal-your-...
And the attack in OP does not require iframes, so it can also be applied to injection attacks where CSP prevents javascript for example.
(disclaimer: author of story)
> it's usually the opposite with it being not even accepted by many bug bounty programs.
As someone who has been on the other end of bug bounty's, its because clickjacking reports are a massive spam magnet. 99% of reported are not really vulns (e.g. no xfo header on a static website with no user auth, is not a vuln), and its just not worth sorting through.
> I've been able to make realistic attacks against multiple targets. Many services, such as Google Docs, need to enable cross-origin framing for their functionality.
The google docs thing is really cool. However i think services that need authenticated frames are few and far between. Now that cookies on frames tend to be opt in, i think the number of vulnerable services is going to go way down. Its not going to be 0, but its going to be pretty limited.
A valid report needs to demonstrate a realistic attack scenario, and I think that's the approach bug bounties should take.
I think a good example is Google with its stance on open redirects[0]. They won't accept a report just pointing one out, but they will accept one that "can demonstrate that its impact goes beyond phishing".
[0] https://bughunters.google.com/learn/invalid-reports/web-plat...
What specifically does Google Docs do that requires it?
> And the attack in OP does not require iframes
How do you frame the victim site without iframes?
Google wants documents to be embeddable on external sites.
> How do you frame the victim site without iframes?
You don't, you use it in a different scenario. For example if you have HTML injection, but its fairly limited due to strict CSP.
Researchers have observed that, in Chrome:
A hostile webpage can create SVG or CSS filters that cover an iframe on the same page and act on the iframe's content.
Specially-crafted filters can be created that vary their performance characteristics (different use of memory bandwidth or compute resources) based on input data.
The induced differences in load can, in turn, be used to leak the input data through a timing sidechannel readable from Javascript.