simonw18 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...
rafram17 hours ago
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.
amluto15 hours ago
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.)
bawolff15 hours ago
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
amluto15 hours ago
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.
tracker117 hours ago
Nice, favorited... thinking this could be useful for an email reader to support css, but not scripts.
arcfour11 hours ago
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.
recursive17 hours ago
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.
rafram17 hours ago
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.
simonw17 hours ago
You can entity-encode the content in the srcdoc= attribute to robustly solve that problem, or populate it via the DOM.
tracker117 hours ago
s/"/"/g
andybak18 hours ago
My first thought is "support a tiny subset of svg that probably still covers 90% of real-world use cases".
I do feel that's there's two distinct types of svg - "bunch of paths with fills" and "clever dangerous stuff" where most real SVGs are of the former type.
Fully expect this to be shot down by someone that's thought about this problem for longer than the 120 seconds I just spent. :)
gardaani3 hours ago
W3C has been defining SVG Native, but it hasn't progressed much lately — mostly because there hasn't been any interest in it. SVG Native is a small subset of SVG 2.0 which doesn't support scripting, animations or any external references. https://svgwg.org/specs/svg-native/
pupppet14 hours ago
This is what happens when there isn't an adult in the room to reign things in, you get project overreach. SVGs should never have supported scripting. You want scripting in SVGs, fine, make it a different file format.
I can't imagine the cumulative number of man hours wasted on this problem when the vast majority of users were just looking for a way to make their logos look sharp.
thrance14 hours ago
Or you can literally just manipulate your SVG through the DOM in an external JS script... I still have no idea what the original motivation behind scripts in SVGs was.
zarzavat10 hours ago
While SVG is a web technology, for the longest time you had to install SVG support as a browser plug-in. I remember installing Adobe SVG viewer around 2000. It was used for interactive visualizations.
I'm don't remember precisely but I don't think you could script it from the DOM, I don't see how that could work if it's a plugin.
bobthepanda13 hours ago
I imagine it may have been attractive to those who liked Flash.
pbhjpbhj2 hours ago
I think it may have been the other way (ie attractive to those who didn't like flash) - SVG was seen as a potential flash replacement?
oldsecondhand9 hours ago
OG actionscript was very similar to Javascript. It only started to diverge when type hints were introduced.
gsnedders7 hours ago
AS2 was mostly following the direction of ES4 — so it wouldn’t have diverged if it hadn’t been abandoned.
NL8078 hours ago
> SVGs should never have supported scripting.
I would even go further: HTML should never have supported scripting.
throwawayqqq115 hours ago
... or third party requests. Scratch the H in HTML and internet tracking would have never happened.
afavour18 hours ago
I think you're right but the lack of industry standard for this kind of thing kills it. People want to be able to take the output of whatever tool they use that exports SVG and put it in a browser. Which isn't an unfair request. But you wouldn't have a guarantee it wouldn't filter out the tool using some obscure SVG functionality.
I'd love to see an agreed standard like OpenGL vs OpenGL ES for SVG. SVG-ES. Everyone agrees on the static, non-scripted elements that should work.
varun_chop18 hours ago
The way linked SVGs render from within img tags is basically perfect for SVG images (which as I understand is not standardized but is largely the same across browsers). External resources and scripting are blocked while still rendering nearly all SVGs correctly. And of course, any CSS is scoped to the SVG.
If someone formalizes this as a new format, please give it a new name! tvg tiny vector graphics? savg safe vector graphics?
And keep the scope as simple as possible so it actually ships! Don’t try implementing a binary format or something.
hackeman30018 hours ago
Someone did this already and did call it tinyVG! https://tinyvg.tech/
[deleted]3 hours agocollapsed
sbrother16 hours ago
Maybe I'm missing something as I am not a frontend developer, but when you embed SVGs in an img tag as part of a Phoenix LiveView or even just a static component, you no longer get the ability to dynamically change paths/fills/colors with events coming from the server. Even if it's as simple as having a shape that you want to fill with a brand/highlight color, which at least for me is a common use case.
ambicapter18 hours ago
.rvg, Restricted Vector Graphics?
[deleted]17 hours agocollapsed
bawolff15 hours ago
> My first thought is "support a tiny subset of svg that probably still covers 90% of real-world use cases".
It sounds like the linked post was about someone using a blacklist instead of a whitelist. It doesnt matter how tiny your subset is if you allow through stuff you don't recognize.
For the most part svg is safe. The dangerous parts are pretty obvious - script tag, image tag, feImage tag, attributes starting with on, embedding html in <foreignObject>, DTD tricks, namespace tricks, CSS that loads external stuff (keep in mind also presentational attributes. Its not just style attribute/tag).
The rest of it is pretty safe.
hackeman30018 hours ago
Seems like someone already implemented your idea. https://tinyvg.tech/
codedokode6 hours ago
Maybe we could use a subset of SVG or PDF?
Avamander15 hours ago
There's the SVG Tiny profile that some implementations use, like BIMI/VMCs.
harperlee18 hours ago
Fwiw I just thought the same, parse (don’t validate) the bits you like and recreate / reject the input.
nine_k8 hours ago
I would say that a proper sanitizer should remove any attribute that has /https?:/ in it. Maybe it should allow access to a subtree of a blessed domain you control, where stuff like textures is stored.
0x1ceb00da6 hours ago
This is what android does. It has its own vector asset format and android studio has an action for importing svgs.
varun_chop18 hours ago
I wonder if it would be best if this was at the browser level as some sort of new format. Otherwise surely it would be really slow/cumbersome to deal with these in ‘user space’
whycome18 hours ago
It always seems like any animated svg loses all of the animation after sanitizing
RobotToaster4 hours ago
A lot of SVG animation uses JS for some reason. It would be interesting to see if sanitisers strip CSS and SMIL animation, I don't see any security reasons to do so.
bawolff15 hours ago
There are 3 different methods of animating svgs so it probably depends.
Nursie8 hours ago
SVG Tiny PS (Portable Secure) is an attempt at this - https://www.ietf.org/archive/id/draft-svg-tiny-ps-abrotman-0...
Though I think it's still a draft, it does appear to be a requirement for BIMI - https://en.wikipedia.org/wiki/Brand_Indicators_for_Message_I...
LorenPechtel18 hours ago
Yeah, I think that's the real answer.
Look at what Microsoft did with Excel--the dangerous stuff is behind a switch.
Thus, solution:
Add two bits to the tag.
SVG1 does not execute any sort of script.
SVG2 does not follow links.
SVG3 is actually SVG1 + SVG2 as these are bit flags, not numbers.
Additional bits are reserved for future use if any other issues are found.
The only real safety is in the engine, not by any sanitizer.
bawolff15 hours ago
Which is essentially already how it works. See https://svgwg.org/specs/integration/#secure-animated-mode
cachius16 hours ago
What switch?
RobotToaster16 hours ago
You'd lose a lot of useful features, like SMIL animation.
andybak16 hours ago
But you'd gain adoption. A fair trade.
duped18 hours ago
So if you are building something where you control every SVG ever produced and rendered then this is totally reasonable.
If you ever need to interface with other tools that generate SVG you now need to have a way of essentially transpiling SVG from the wild into your tamed SVGs. Oftentimes this is done by hand, by a software developer and designer (sometimes the same person).
And this is for basic functionality that your designers expect and have trivial controls for in their vector editors, like "add a drop shadow."
The article goes into some issues with sanitization itself, and except for <script> these are a bunch of reasonable things that someone might expect to work or not have issues with. Sandboxing rendering isn't an unreasonable approach if you're not writing the parser and renderer yourself.
nmilo12 hours ago
I'm sorry because I love the scratch project but this has to be said: they found XSS in SVGs in a surface with attacker-controlled access to Node and their fix was sanitizing it using regex??? And this was discovered by a user on scratch?
Even worse, OP's latest post "Every version of Scratch is vulnerable to arbitrary code execution" just tells you how exactly to exploit something similar today in the current version with no mention of responsible disclosure except a plug to say, "hey, check out my project, this one doesn't have RCE!" This is so irresponsible it borders on malicious.
inkmuffin11 hours ago
That post mentions that I disclosed this to Scratch in February 2024. The POC in that post is functionally identical to a POC I provided them back then and in various subsequent communications.
evilpie17 hours ago
The HTML Sanitizer API has a subset of SVG that is allowed by the default configuration. It won't help you with sanitizing CSS at all however, style is simply not allowed by default.
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Saniti...
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Saniti...
Grokify8 hours ago
Good reference, along with the article. I built a SVG sanitizer in Go and will look to these to make it more strict.
philo2318 hours ago
It'd be nice if there was a sandbox attribute you could add to inline <svg> tags, like the <iframe sandbox> attribute that'd let you opt out of all the potentially "dynamic" stuff inside of an SVG like scripts and event handlers, or even just literally sandbox the entire thing from accessing the "parent" HTML page's context/cookies/etc just like an iframe.
I'm sure it'd just open up a whole other can of worms though... not to mention having to wait for browsers to actually support it.
The real solution here is definitely CSP + basic sanitisation though.
chocmake17 hours ago
Most of the aspects the author was critiquing are actually just regular CSS features, they simply don't want any external requests. Effectively they want inlined SVGs to be treated like how the browsers treat IMG-embedded SVGs (no scripting or external requests loaded).
Sanitization-wise it's already possible to strip scripting from SVGs and anything else you want, it's just that a library like DOMPurify to avoid ballooning in size doesn't include say a preset to handle the extra parsing necessary to make them behave like browsers treat IMG embeds, so it's up to devs to add their own.
But yeah, a world where a simple attribute to achieve the same effect as an IMG embed but for inlined SVGs would be nice.
rslashuser13 hours ago
Exactly. It's not a good solution where you have to read a bunch of steps to do to make SVG safe, where you're worried you forgot one. Instead there should be a straightforward <svg exec="false"> or whatever that simply and comprehensively disables the unsafe features.
Think of prior technologies like display postscript and .doc, where a data format ended up a with big problems from its embedded "exec" type features.
xigoi5 hours ago
Why not just use an <img> tag?
simonw17 hours ago
Thankfully if you have CSP you don't need even basic sanitization, which is useful because most of the problems in this article are demonstrations of how simple sanitization isn't simple at all.
bawolff15 hours ago
I dont see how that could work, as an <svg> tag in html is not a document boundry. How can you prevent it from accessing a parent doc when its not a separate document.
There is iframe srcdoc if you want to do this.
philo2315 hours ago
> How can you prevent it from accessing a parent doc when its not a separate document.
By turning it into a document boundary when you use the sandbox attribute, kinda similar to loading an svg file inside of an <img> tag.
and yeah you could get 90% of the way there with an iframe srcdoc, but I was imagining some kind of cross between an <iframe> sandboxed into its own origin, and an <img> where it still has its own intrinsic size.
but it was mainly just a throw away thought, I've not really thought it through much deeper than that.
bawolff12 hours ago
Wouldn't that be the exact same as an <img> tag? Img tag disables all scripting.
somat14 hours ago
img src="file.svg"
does that work for you?
pornel7 hours ago
This is not sufficient to stop XSS, because an attacker can redirect users to file.svg URL as the top-level document, bypassing restrictions of <img>.
If you're going that route, add CSP headers on HTTP level to disable scripting, and/or host the SVG on a separate domain that has nothing valuable, or use data: URLs.
philo2312 hours ago
The main use case I was thinking of is being able to use an inline SVG, but with external resources inside of it (like say a CSS background image using url(...)) in such a way that it ends up loading that embedded content in a cross-origin anonymous way and blocking all embedded scripts. That way someone can't make requests to CSRF exploitable URLs by setting an embedded images to something like example.com/my-submission/favourite
But also so that setting up a CSS transform: scale(10000) can't take over the entire viewport, it'd be constrained to an iframe-like boundary (exactly like an <img>) but still remain as an inline SVG, sort of like an <iframe srcdoc>. So scripts on the parent/host HTML document can still manipulate it like the rest of the DOM, but the inner <svg> elements are all "inert" for want of a better word.
Actually I don't know off the top of my head what happens with an SVG file inside of a <img> when it references external images (either cross-domain or not.) I know scripts and animations get disabled, so I'd take a guess and say some CSS gets blocked too.
Again I've not really thought terribly hard about it, or if it's actually useful at all, and I'm betting it'd be filled with even more foot-guns than there are right now. I'm just thinking out loud.
[deleted]11 hours agocollapsed
ikkun18 hours ago
I do wish tinyVG or similar would take off, but I don't see that ever actually happening. the only thing I think it's missing is animation support, which is pretty niche but not as niche as <script> tags.
spankalee19 hours ago
This is, by the way, why Google Slides doesn't have SVG support even though there's a nearly 15 year old ticket requesting the feature.
esprehn8 hours ago
They totally could sanitize it, just like they did with the gadget proxy, they just choose not to fix this ancient request.
It's like my ticket to add data url support to sheets. It just gets punted year after year.
ssocolow18 hours ago
djoldman10 hours ago
Cloudflare went down this road a bit:
codedokode6 hours ago
I don't like that SVG uses things like CSS and JS and requires pulling in the whole browser to display. Instead of being a simple vector image format, it became just an extension of HTML. Maybe we need a new format, and if someone decides to do it, please add ability to embed fonts, wrap text and decent animations.
Springtime18 hours ago
It seems the reason they're inlined in the page at all is to measure things briefly like bounding boxes (not sure the full extent as it didn't cover that), before subsequent removal. I'm not familiar with Scratch and its use of user-submitted SVGs but I'd be curious to read more about what they're doing that required it be inlined specifically.
(This isn't a comment on the challenges in proper sanitization fwiw, as I've needed to do various of the same things myself)
inkmuffin11 hours ago
They want to run getBBox [1] which requires the SVG to be in the DOM somewhere - otherwise it throws an error. They need to do this because SVGs tend to have very inaccurate viewboxes, especially when working with SVGs made in old versions of Scratch. getBBox is the easiest way to get a more accurate understanding of how big the stuff in the SVG is.
[1]: https://developer.mozilla.org/en-US/docs/Web/API/SVGGraphics...
wingian hour ago
thank you for this post.
bawolff15 hours ago
These aren't really SVG specific issues. They are all pretty standard XSS that apply to html and are very well known vectors.
Like this post didn't even mention presentational attributes, like how cursor attribute can contain a url that gets loaded. Or any of the other tricky parts of svg sanitization, like using dtd to bypass things.
Liftyee13 hours ago
I'm not familiar with the details of real software development, so I don't know why it's not possible to just "not give the SVG part of the code internet access" or "perform sanitization on post-decoding (url, hex, etc) data".
Is it because the SVG parser/renderer being used is an entire library, and it would be prohibitive to write your own SVG parser/renderer or insert your own code into the existing one?
drfloyd5112 hours ago
Some of the suggestions are kind-of exactly that. But they specify not a change to the default behavior, but a new behavior based on the presence of a new attribute.
You could change the default behavior to the “safer” behavior. And then add some sort of “danger mode” attribute. But… devs are usually hesitant to do something that would break legitimate code, such as changing the default behavior would do.
kevinmgranger17 hours ago
> This was fixed by using a regular expression to remove script tags.
The infamous you can't parse (X)HTML with regex¹ meme is from 2009, yet this fix was done in 2019. I guess the SO answer never mentioned SVG.
jancsika17 hours ago
For the "<script>" stuff: regardless of how the thing is spelled or otherwise obscured, the HTML5 parser eventually knows when it's gotten hold of a script tag. Oops, we got one in a NOSCRIPTTAG context. Let's poop out.
Tag names, attributes, attribute values, event callback default-cancelers... so many ways to declare that this node and its children shouldn't parse/evaluate scripts.
As Jay-Z said: "I've got 99 solutions, fixing a problem ain't one"
etchalon18 hours ago
I don't understand why it wasn't immediately understood that SVG is as dangerous as HTML.
It is not, and never was, an image format. It's a markup language.
nulltrace11 hours ago
Browsers already treat the same SVG differently depending on how you embed it. <img> strips scripts and external resource loads. <object> and inline don't. People test with img tags, looks fine, then someone switches the embed method and everything opens up.
OneDeuxTriSeiGo9 hours ago
it'd be nice if there was a way to declare in the URL that a given SVG could only be treated as an image so that you could safely open SVG urls, etc without exposing yourself to the dangers of embed/inline.
xigoi5 hours ago
Couldn’t you do that using Content-Security-Policy?
OneDeuxTriSeiGo4 hours ago
If you control the domain then yes you could. But if I want to put a link on my website to some SVG hosted elsewhere and I want it to be safe for you to open that link in a new tab then there's not really a way for CSP to protect you the user from the host deploying a malicious SVG.
Like opening a PNG in a new tab is harmless but opening an SVG in a new tab is opening a pretty substantial can of worms.
xigoi4 hours ago
If your threat model is “I don’t want the image I’m hotlinking to be replaced with something else when opened in a new tab”, then no image format is safe.
recursive14 hours ago
A markup language can be an image format. The "G" is for "Graphics" after all.
NooneAtAll316 hours ago
wait... scratch is just a browser?
inkmuffin10 hours ago
Since 2019, Scratch is written to run in a standard web browser, replacing the older Flash runtime/editor. The desktop app uses Electron.
Theodores16 hours ago
Maybe we need a dumbed down version 3 of SVG where the browser knows it is not to do anything that requires fetching a URL, to make the image as harmless as a JPG.
This version 3 could have the version number changed to 2 in order to do cool SVG things, so full-fat SVG as version 2 is now. But you could just flip to 2 to a 3 on upload, so any embedded URLs are harmless.
This could be useful for the creator too, as it is helpful to have layers of source images in bitmap format to work with, and you can easily export such things accidentally.
Devasta16 hours ago
> In 2019, a few months after the initial release of Scratch 3, Scratch discovered that SVGs can contain <script> tags that Scratch would cause to be executed when the SVG loads. This is known as an XSS.
> Example from Scratch's test suite:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="250" cy="250" r="50" fill="red" />
<script type="text/javascript"><![CDATA[
alert('from the svg!')
]]></script>
</svg>
Is this really an issue? This is the method that the chrome teams polyfill to replace XSLT suggests you do. https://github.com/mfreed7/xslt_polyfill/tree/main#usageinkmuffin11 hours ago
This was the example from their test suite. I didn't want to clone and build a 2019 copy of Scratch to test it end-to-end since the specifics weren't super important anyway.
esafak18 hours ago
Is there a browser-friendly vector alternative?
simonw17 hours ago
SVG in an <img> tag can't execute scripts.
esprehn8 hours ago
It also can't inherit css variables which is unfortunate since it means the image doesn't respect the theme.
marlburrow8 hours ago
[dead]
nengil18 hours ago
[flagged]
shaguoer17 hours ago
[flagged]
SpyCoder7717 hours ago
I did not expect to see GarboMuffin.