[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("&lt;".length)</script>
       | 
       | In HTML with HTML syntax, this will alert 4, the string being
       | "&lt;".
       | 
       | 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 &lt; 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)