[HN Gopher] SVG images can contain JavaScript
___________________________________________________________________
SVG images can contain JavaScript
Author : mooreds
Score : 82 points
Date : 2024-01-21 16:33 UTC (6 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| solardev wrote:
| Good. I'd hate for it to break out of containment!
| smartmic wrote:
| Tangentially, I learned a few days ago that any attribute that
| can contain a URI will be executed as javascript if the URI uses
| the javascript: key
|
| For a list, see
| https://stackoverflow.com/questions/2725156/complete-list-of...
| moritzwarhier wrote:
| This is true, but also so old that it is well known by now.
|
| It can be blocked by CSP IIRC.
| wongarsu wrote:
| That SVG can execute Javascript also used to be well known,
| back when SVG was considered the future replacement to Flash.
| I even remember some websites that were just one handcrafted
| SVG. And browsers deciding not to execute these scripts when
| you load an SVG via an <img> tag was a big deal.
|
| Of course SVG didn't become the Flash replacement everyone
| hoped for, so that knowledge seems to have become more
| obscure. The same probably happened to knowledge about
| javascript links after frameworks like react made event
| handlers the more convenient alternative.
| moritzwarhier wrote:
| Yeah I once went down the rabbit hole of googling SMIL and
| there's a nice example on Wikipedia of a live clock, right?
|
| At the time, this example even worked on my phone, will
| have to check if that's still the case.
|
| Generally, SVG reminds of PDF with its feature creep
| (foreign entities?)... but I feel web browsers are beating
| SVG into becoming a sane vector graphics format without
| such shenanigans.
| jarek-foksa wrote:
| SVG animation elements are now mostly supported by all
| modern browsers. You can even use `transform-origin` and
| `transform-box` CSS properties to control the origin
| point (this was a major pain point in the past). I think
| "paced" interpolation is the only important feature that
| is still missing.
| andybak wrote:
| The example SVG: https://berthub.eu/trifecta/i/Tcp2y3TbBzU
|
| doesn't execute the javascript when visited directly in any
| browser I tried. Does it make a difference if it's embedded in a
| html page?
| masklinn wrote:
| If linked via an <img> tag, browsers will run the SVG in a
| script-less context. The problem is direct linking, then you
| have a potential XSS.
|
| Here it's the CSP directive (Content-Security-Policy: script-
| src 'none') which is neutering it, as indicated by the comment
| your got the SVG from.
|
| However from what I remember of my experiments script-src none
| can severely degrade SVGs even though they don't use JS,
| because it affects some styling.
| tlhunter wrote:
| According to the page the HTTP CORS headers prevent JS
| execution. But the problem remains that svg can embed and
| execute JS on servers without these non-default headers.
| lol768 wrote:
| CSP, not CORS.
|
| CORS is for allowing other origins to read resources (and
| send special requests/special headers). It's not really
| relevant here.
| geor9e wrote:
| If you save it locally then drag it into your browser, it will
| run.
|
| Bert blocked it from running when hosted on his domain a couple
| days ago
| https://github.com/berthubert/trifecta/commit/ed7a10d783dda2...
| omoikane wrote:
| If you use mesh gradient with Inkscape, it will append about 17K
| of Javascript to the end of the SVG that starts like this:
| <script id="mesh_polyfill" type="text/javascript">
|
| It surprised me the first time I saw it.
|
| The script appears to be minimized version of what's here:
|
| https://gitlab.com/inkscape/inkscape/-/tree/master/src/exten...
| lol768 wrote:
| Does it warn you? This sort of "magic" could do with a giant
| disclaimer really..
| omoikane wrote:
| You can tell Inkscape not to insert those scripts (edit ->
| input/output -> SVG export -> uncheck the two options for
| inserting javascript), but you have to know to look for them,
| and I suspect most people don't look at the output SVGs.
|
| These compatibility polyfills seem harmless so it doesn't
| bother me, but maybe you are asking if Inkscape can be used
| to pass arbitrary javascript to unsuspecting viewers without
| warning? The answer to that appears to be "yes" via the
| extensions system.
| lol768 wrote:
| It's not malicious, but if you're expecting to be able to
| use the SVG in the context of an <img> element, or on a
| site with a strict (in line with security best practice)
| Content-Security-Policy, this is just a sharp edge that you
| can cut yourself on because the polyfill simply won't work
| in those contexts.
|
| I know I'd be annoyed if I realised my image editor had
| "tried to be clever" and done this under the hood without
| telling me.
| ironhaven wrote:
| If anybody wants to see an intended use of javascript in SVG
| images take a look at flamegraph profiles[1]. Very useful to find
| program hotspots when you can click to focus on particular code
| paths.
|
| [1] https://brendangregg.com/FlameGraphs/cpu-bash-flamegraph.svg
| andybak wrote:
| Still not clear why the javascript needs to live inside the
| SVG. In my mind, the SVG would have declarative metadata and
| some external javascript would attach behaviour.
| cptroot wrote:
| Notably, there's significant restrictions on loading external
| <script> tags if the original file was loaded through
| file://. This means that your idea would likely require a
| static file server or similar.
| Evidlo wrote:
| Not nearly as portable as what we have now.
| DougBTX wrote:
| Agreed, at some point custom "declarative metadata" just
| becomes a nonstandard onclick handler.
| Brian_K_White wrote:
| So a single .svg file can actually be a whole app? Wow we
| reinvented flash.
| Twirrim wrote:
| That's part of the original design goals for it. Scripting
| support has been part of it from the very beginning, to
| allow for time-based animation etc.
| cbsmith wrote:
| While scripting could be used for time-based animations,
| the preference was to use the declarative SMIL. Scripting
| support was intended to allow more sophisticated
| interactive graphics.
| numtel wrote:
| It would have probably taken off if there was a way to have
| wrapped text in the early days.
| cbsmith wrote:
| SVG was the result of an effort to make an open standard
| for vector graphics. As is typical for open standards, it
| wasn't an entirely novel concept. There were several
| competing alternatives; IIRC, SVG was one of half a dozen
| proposals submitted, and most of those proposals were
| evolutions of prior designs/attempts at a standard. While
| flash was an original design, it was as much a "reinvented
| Shockwave" as SVG was a "reinvented flash".
| arter4 wrote:
| Agree. What (legitimate) use case does Javascript inside an
| SVG address?
| cbsmith wrote:
| That sounds like a workable model for vector graphics in
| browsers; it's just not describing SVG's design. It very
| deliberately was meant to contain not just vector model, but
| also a dynamic/interactive environment.
|
| From the standard: https://www.w3.org/TR/SVG11/intro.html
|
| "Sophisticated applications of SVG are possible by use of a
| supplemental scripting language which accesses SVG Document
| Object Model (DOM), which provides complete access to all
| elements, attributes and properties. A rich set of event
| handlers such as 'onmouseover' and 'onclick' can be assigned
| to any SVG graphical object. Because of its compatibility and
| leveraging of other Web standards, features like scripting
| can be done on XHTML and SVG elements simultaneously within
| the same Web page."
| rjsw wrote:
| I have been told that another intended use of javascript was to
| size a visual element like a rectangle to be the bounding box
| of a text string.
| fmajid wrote:
| It's also XML-based and thus potentially vulnerable to XML XXE
| inclusions.
|
| XML, the over-engineered-by-committee gift that keeps giving.
|
| Fun fact: until recently my boss' boss Vincent Hardy was a co-
| editor of the SVG spec.
| WillAdams wrote:
| When doing the assembly instructions for the opensource CNC
| machine the Shapeoko 2:
|
| https://shapeoko.github.io/Docs/
|
| I worked up a mechanism which allowed opening SVG files in
| separate windows where they were interactive so that one could
| click on an entry in the parts list and have the matching parts
| in the diagram highlighted.
|
| (but for some reason the site isn't loading at the moment)
| chrismorgan wrote:
| One fun thing to be aware of is escaping requirements.
| <script>alert("<".length)</script>
|
| In HTML with HTML syntax, this will alert 4, the string being
| "<".
|
| In HTML with XML syntax (yes, still a thing), or in SVG
| standalone, or in SVG in HTML of either syntax, this will alert
| 1, the string being "<".
|
| There's a lot more one can talk about with this, HTML parser
| details, script data double escaped state, CDATA, but I'll leave
| it at that. Just don't be surprised when you see this sort of
| thing in your SVG: if (a < b) { ... }
|
| Allowing users to place JavaScript inside a <script> tag in HTML
| but preventing them from breaking out of it is rather difficult,
| requiring semantic handling of the JavaScript and some rather
| convoluted changes which _could_ still break things. Similar deal
| on CSS, just that it _can_ be done perfectly since you don't have
| to worry about Function.prototype.toString detecting the changes.
| But no, if you want to do such a thing, the sensible and
| principled way is either to serve the document with XML syntax
| (though there are various problems with this and I don't advise
| trying), or to use SVG's <script> or <style> instead of HTML's,
| and do regular HTML entity encoding of the source.
| <svg><script>...</script></svg>, easy enough.
| elric wrote:
| There are quite a few ways in which SVGs can contain evil things.
| iframes, script tags, the use tag (can load external sources),
| links with javascript, links with _base 64 encoded javascript_ ,
| bloody DOM event handlers (on-click etc).
|
| At my previous job, user uploadable SVGs were the largest source
| of vulnerabilities in our product.
|
| My unwanted advice: don't let users upload SVGs unless you know
| what you're doing. We clearly didn't.
| cbsmith wrote:
| I'm kind of confused by the title. This shouldn't be a
| surprise/news as SVG explicitly uses JavaScript as its scripting
| language. That's why there's an SVG DOM. Yes you can do
| animations with SMIL, but JavaScript is explicitly there for more
| complex interactions.
|
| https://www.w3.org/TR/SVG11/script.html
| stigma wrote:
| Walkthrough of remote code execution in Scratch Desktop using
| JavaScript in SVGs:
| https://web.archive.org/web/20220416143159/https://www.mnemo...
| (2021)
| jancsika wrote:
| Suppose that all browsers hide javascript-inside-svg behind a
| preferences checkbox that's turned off by default.
|
| What on the web breaks?
|
| Most of the scripting related to SVGs I've seen are SVGs
| controlled by javascript in the containing HTML, usually through
| an HTML framework.
|
| Hell, in the history of the web has there _ever_ been a framework
| written specifically for the javascript-inside-svg case?
|
| The irony is that svg has not gained generally usability as an
| uploadable image format precisely _because_ of this feature that
| nearly no one uses.
| skrebbel wrote:
| For this reason, it's a good rule of thumb to always serve user-
| uploaded files from a different top-level domain, one which has
| no session cookies and localstorage and the likes. This way, if
| someone manages to upload a file with JS in it, and trick a user
| into opening it, the JS cannot do any harm because it doesn't
| have access to anything.
|
| This won't stop an onslaught of incompetent security researchers
| from contacting you about an XSS vulnerability, because they
| think that XSS means anything that can do `alert` and they don't
| understand what the XS in XSS stands for, but meanwhile you're
| secure even if you forget (or chose to skip) sanitation.
|
| I recognize that this feels a bit out of scope for a self-
| contained c++ image sharing service, but if you have a more
| classical cloud setup, you get this for free simply by serving
| your user-uploaded files from some object storage bucket.
| skrebbel wrote:
| Addendum: this is safe because browsers only run scripts in
| SVGs when the SVG is served as a page, and not when it's
| embedded in a different page as an image or background.
|
| Doing <img src="evil.svg"> is always safe.
| yieldcrv wrote:
| you can but its not widely supported
|
| and sometimes for security reasons already
|
| been down that rabbit hole when making interactive NFTs without
| externally hosted images, since that was one of the _many_
| criticisms of common implementations of NFTs
| Jasper_ wrote:
| People forget that SVG was not just meant as a vector graphics
| format. It was meant as Adobe's competitor to Flash. Adobe wrote
| most of the original specification, runtime ("Adobe SVG Viewer"),
| and tooling ("Adobe LiveMotion").
|
| Animation, scripting and multimedia are still in the spec because
| Flash had those.
| starkparker wrote:
| For people learning about this today, be sure to see the script-
| in-SVG quirks, restrictions, and support (or for some features
| like modules, lack thereof): https://developer.mozilla.org/en-
| US/docs/Web/SVG/Element/scr..., https://developer.mozilla.org/en-
| US/docs/Web/SVG/Scripting, and
| https://web.archive.org/web/20100223210744/http://wiki.svg.o...
| olliej wrote:
| Many many aeons ago when I was still working on the webkit SVG
| implementation[1], we had to deal with this. Essentially all
| other complex "images" are in isolated worlds from the page
| itself, e.g. they're static images (no issue at all), or a
| sequence of frames that require periodic repaints[2].
|
| Now logically an SVG "image" is a webpage, and you could easily
| just embed an iframe or such like (I _think_ <object> also
| worked? long time ago now) and it would just work because the
| browser engine understands that an iframe means "time for a whole
| new world". A big problem for SVG images in image tags is that it
| could just arbitrarily make any <img> tag also now have the same
| essential "I'm a full web page now" semantic, which was really
| something webkit wasn't really ready for at the time. The
| original implementation was not super great and I hope/assume
| it's somewhat better now, but at the time we essentially made it
| so we had a hacked in a image element subclass that would copy
| the render image from a pseudo iframe. There are a whole bunch of
| other issues that had to be addressed, and also at the time
| weren't covered by any specification, like what happens with
| event handlers in the SVG vs the Image element.
|
| Now you run into performance problems. The overwhelming majority
| of SVGs you hit are entirely static, a small subset have
| declarative animation, so now we have a question: do we really
| want to burn the cost of instantiating creating essentially a
| full iframe for every SVG image? that impacts load time and
| memory use so you want to avoid it if possible, and in principle
| the engine can detect whether the SVG image contains any scripts
| or event handlers, etc. But if you do try to do this, then you
| hit issues if the embedding page then tries to interact with the
| image, and trying to handle that kind of change dynamically would
| involve that kind of complexity that leads to errors (oh for
| safer memory handling in C++, but at least these days there are
| better options everyone is working on).
|
| IIRC the end solution that every browser went for was to just say
| "screw this" and drop most SVG features from SVGs loaded through
| image elements.
|
| One thing that people don't understand when looking at SVG, is
| that they _think_ it's an image format, but the reality is that
| was never the actual intent of the specification, and that's why
| SVG is so insanely complex.
|
| Many aeons ago people were like "what we really need is a
| standard format for vector graphics", and then some people said
| "it should support animation". If they stopped there you'd just
| have an image format. But the commercial groups that worked on
| the standard, wanted a more, for a few reasons. The big party I
| saw referenced a lot in the past was Adobe (or Macromedia at the
| time?). They didn't seem meaningfully involved in the SVG-WG when
| I was a member so I don't know how accurate the claim is, but
| essentially what people have said is that Adobe/MM was actually
| interested in SVG as essentially a future for Flash so wanted the
| specification to be feature compatible, but also because
| capitalism wanted to sell their software and so pushed for a
| specification that was "human writable" but fundamentally you
| would need well funded commercial software to actually develop
| content. That seems plausible in the "capitalism is evil" sense,
| but also the design space for "technical standard", "supports
| everything flash can do", and "uses xml" is super awful and
| complicated so I don't think that the complexity and weirdness of
| SVG is direct evidence of that.
|
| The _much_ _much_ more interesting party that was involved in the
| SVG WG were japanese makers of mapping software for phones (from
| prior to and overlapping iPhone era browsers), or such companies
| targeting the japanese market. I do not understand the exact
| specifics of the legal (or maybe just contractual?) constraints
| that they were operating under, but the general gist was that
| they needed their mapping software to be entirely in terms of
| actual standards. And this is where the feature additions in SVG
| go insane. The constraints they were under meant that everything
| they did had to be a standard, but any implementation had to
| support the _entire_ standard. At the time SVG started, and for a
| long time after I had become involved in the SVG WG and then
| escaped, the html specification was a single standard, so the SVG
| spec could not just (for example) reference XHR even when it was
| standardized. As a result for these manufacturers the SVG spec
| could reference the XML and ECMAScript specs, and then
| essentially everything else had to be entirely self contained. So
| if it needed network APIs in JS, they needed to specify them
| internally, and long story short, that's how the SVG
| specification got raw socket access (no browser ever supported
| this :D).
|
| It's also how SVG got profiles: these manufacturers had to be
| able to say "we fully implement this standard" but at the same
| time this is meant to be an image format for actual art and such,
| and so it needed complex blends that these manufacturers could
| not afford (in terms of hardware perf). Unfortunately these
| profiles were often mutually incompatible, even for basics like
| blending, because the profiles were selected on the basis of cost
| with no consideration of interaction.
|
| Anyway, that's a very abbreviated tale of how the "image format"
| SVG became the gigantic and complex specification it is.
|
| [1] This came from importing ksvg2 into webkit, and the constant
| refrain of "webkit just being a fork of khtml" could much more
| legitimately have applied here. In terms of compatibility with
| content that actually existed ksvg2 was far more complete than
| khtml ever was, and the general dearth of non-static or generally
| complex SVG content meant that for years very little work
| actually needed to be done on the merged ksvg2 logic beyond
| tracking changes in the rest of webkit. By the first release of
| Safari a huge amount of basic compatibility and perf work had
| been done, by Safari 2 large rearchitecting had also occurred,
| and around the time I started contributing (while avoiding my
| thesis) KJS was also being significantly rearchitected. By the
| Safari for Windows release I would say in essence khtml had been
| rewritten, and I think JSCs only real remaining architectural
| link to KJS was that it was still an AST interpreter, but I think
| that was changed by Safari 4? I could be a little off on timing
| vs releases because this was a long time ago, but even by that
| point in history the claims of webkit being khtml were simply not
| accurate let alone today. Bringing it to modern terms by the
| point of Safari 1's release WebKit was to KHTML as Blink today is
| to WebKit.
|
| [2] Apple's webkit used to also support pdfs as images because it
| just through all "image" data at CoreImage or ImageIO that just
| sniffed the image type and would work out how to decode a much
| larger set of image formats than anyone would ever think
| reasonable. I'm not sure how much that has been cut down now, but
| this is how things like that OpenEXR vulnerability happened -
| ImageIO supported OpenEXR a format that wasn't generally
| considered a "web" image format, but if you throw it at ImageIO
| it would parse it. ImageIO also supports a wide variety of RAW
| image formats, and you could have those directly as well.
| mardifoufs wrote:
| That is super interesting. Is there some sort of "lower common
| denominator" svg standard used nowadays on browsers? Is there
| any implementation that also implements the entire spec?
| olliej wrote:
| Every browser implements the full profile, and all of the
| non-insane parts. I think it was the defunct SVG2 attempt
| that may have had raw sockets.
___________________________________________________________________
(page generated 2024-01-21 23:02 UTC)