Comment by simonw
18 hours ago
I'm glad this article includes the only credible fix for the HTTP leak problems: CSP.
A useful thing I learned recently is that, while CSP headers are usually set using HTTP headers, you can also reliably set them directly in HTML - for example for HTML generated directly on a page where HTTP headers don't come into play:
<iframe sandbox="allow-scripts" srcdoc="
<meta http-equiv='Content-Security-Policy'
content='default-src none; script-src unsafe-inline; style-src unsafe-inline;'>
<!-- untrusted content here -->
"></iframe>
It feels like this shouldn't work, because JavaScript in the untrusted content could use the DOM to delete or alter that meta tag... but it turns out all modern browsers specifically lock that down, treating those CSP rules as permanent as soon as that meta tag has loaded before any malicious code has the chance to subvert them.
I had Claude Code run some experiments to help demonstrate this a few weeks ago: https://github.com/simonw/research/tree/main/test-csp-iframe...
I read this whole post silently mouthing a "CSP" mantra as each new vulnerability was discovered, years apart no less. Elated when I got to the revelation towards the end.
But for all my self righteous bluster the inline version was news to me. Hacker news. Awesome. Thank you.
Wow - I had no idea. That's really useful, and probably much easier to implement by javascripters than something that might be set in nginx.
And any additional CSP directives can only narrow what's allowed. Also works with headers plus <meta> - <meta>s can restrict the CSP even more than what the headers specified, but they can't widen it.
An idea I’ve been kicking around (which isn’t quite applicable to this use case, I think) is to aggressively restrict the Sec-Fetch- headers on user content. If a server is willing to serve up an untrustworthy SVG, it could refuse to serve it at all unless Sec-Fetch-Dest has the correct value, and ‘document’ and ‘iframe’ would not be correct values. This would make it more difficult to fool a user or their browser by, for example, linking to an SVG file, or using a less-secure mechanism like embed to load it.
This should be in addition to heavily restricting CSP on user content. (Hmm, surely all images should be served with the CSP header set.)
You can bypass the sec-fetch headers via service workers i think.
A better approach here would be to just serve svg with Content-security-policy: script-src 'none'; sandbox
But you can't make a link to https://your.domain/my_phishing_page.svg work as a phishing page using service workers unless you've pretty thoroughly pwned the site already. (And you can constrain what gets to run as a service worker using Sec-Fetch-Dest!)
I suppose an actual exception is Content-Disposition. If you want the user to save a file, you need to serve it with dest == document as far as I know.
Nice, favorited... thinking this could be useful for an email reader to support css, but not scripts.
Most extant email readers support such a limited subset of CSS that nobody is likely to send emails with anything beyond very basic CSS though, unless your reader gains a ton of traction I suppose.
I did not know about `srcdoc`, but it looks like that's still vulnerable to injection by using a double quote and </iframe> to escape the sandbox. If this is constructed in a hygienic way using DOM manipulation, it seems like it could work, but it definitely seems possible to screw up.
If you're constructing your unsandboxed parent document HTML using string concatenation, you might as well not use the sandboxed iframe at all. But presumably someone who bothers to sandbox untrusted content also knows about setAttribute(), or the srcdoc JS property.
You can entity-encode the content in the srcdoc= attribute to robustly solve that problem, or populate it via the DOM.
s/"/"/g