Contents
  1. The short answer
  2. Why an SVG logo can carry XSS
  3. When the script actually runs
  4. Server-side risks beyond the browser
  5. A practical handling checklist
  6. Response headers for stored SVG files
  7. What sanitizing does to the logo artwork
  8. Limits of this guidance

The short answer

An SVG logo is not only a picture. It is an XML document that a browser parses as markup, and that markup can include script, event-handler attributes, script links and embedded HTML. Suppose a site accepts SVG uploads and later serves them from its own origin in a way the browser renders as a document. An attacker can then upload a file that looks like an ordinary logo but runs script in other users' sessions. This is stored cross-site scripting (XSS). Handling SVG uploads safely comes down to four habits. Treat every uploaded SVG as untrusted code. Reduce it to an allowlist of drawing features. Serve it with restrictive headers from an isolated origin. Display it only in contexts where script cannot run.

Why an SVG logo can carry XSS

Raster formats such as PNG and JPEG store pixel data. SVG stores instructions in a markup language that shares much of its behavior with HTML, including scripting. A logo file can therefore contain clean vector artwork and an attack in the same document. The attack may not change how the logo looks.

  • script elements containing JavaScript
  • Event-handler attributes such as onload, onclick or onmouseover on any element
  • Links or href attributes using the javascript: scheme, including on a and use elements
  • foreignObject elements that embed arbitrary HTML inside the SVG
  • animate and set elements that change attributes such as href to a script value
  • References to external files, stylesheets, fonts or images that are fetched when the file renders
  • DTD and entity declarations that XML parsers may expand or resolve

Here is a minimal illustration of an SVG that draws a normal shape and also runs code when it loads as a document:

<svg viewBox="0 0 100 100" onload="alert(document.domain)">
  <circle cx="50" cy="50" r="40" fill="#1a73e8"/>
</svg>

When the script actually runs

Whether an SVG can execute depends on how the browser loads it. When an SVG is referenced through an img element or a CSS background image, browsers render it in a restricted image mode. Scripts do not run there, and external resources are not fetched. When the file is opened directly at its own address, it runs as a full document. The same applies when it is embedded through object, iframe or embed, or when its markup is pasted inline into an HTML page. In those cases script and event handlers can execute. Inline insertion carries the most risk, because the code runs with the full privileges of the host page.

Displaying logos only through img tags therefore does not make an upload feature safe by itself. Uploaded files usually have a direct address, and anyone can send a victim to that address. If it is served from the main application's origin, the script runs with access to that origin's cookies, storage and same-origin requests.

Server-side risks beyond the browser

Problems can also arise before a browser ever sees the file. Thumbnail generators, converters, validators and design tools parse SVG as XML. A parser that resolves external entities can be made to read local files or contact internal services (XML external entity attacks). Deeply nested entity definitions can expand into very large amounts of data and exhaust memory. Rendering libraries may also follow external image or font references during conversion. Turn off DTD processing and external entity resolution, block network access during rendering where possible, and set limits on file size and processing time.

A practical handling checklist

  • Decide whether you need SVG uploads at all. If users only need a displayed logo, accepting PNG or rasterizing on upload removes the scripting risk.
  • Check the file's actual content, not only its extension or the declared MIME type. Reject files that are not well-formed SVG.
  • Sanitize with a maintained, allowlist-based SVG sanitizer, not with hand-written regular expressions or a blocklist of known-bad tags. Keep only drawing elements and presentation attributes.
  • Remove scripts, event handlers, foreignObject, animation of link attributes, javascript: and data URLs in links, and external references.
  • Parse with DTDs and external entities disabled, and enforce limits on size and complexity.
  • Store the sanitized result, and re-sanitize whenever the sanitizer is updated or the policy changes.
  • Serve user uploads from a separate, cookieless domain rather than the main application origin.
  • Send restrictive response headers (see below) so that even a missed payload cannot run in a useful context.
  • Display logos through img elements or CSS backgrounds. Never paste user-supplied SVG markup inline into pages.
  • Generate raster previews or thumbnails in an isolated process without network access.

Response headers for stored SVG files

Headers act as a second layer of defense. A Content Security Policy with the sandbox directive, combined with a ban on scripts, keeps an SVG from executing even when someone opens its address directly. The nosniff header stops browsers from reinterpreting the file as another content type. For endpoints meant only for downloading the original file, an attachment disposition tells the browser to save the file instead of rendering it.

Content-Type: image/svg+xml
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox
Content-Disposition: attachment; filename="logo.svg"

Leave out the Content-Disposition line on addresses that must render the image in pages, and use it on download links. Keep the policy as strict as your logos allow. The inline-style allowance is there because many exported logos use style attributes for fills and strokes.

What sanitizing does to the logo artwork

A well-built vector logo needs paths, basic shapes, groups, fills, strokes, gradients, clip paths, masks and sometimes text. None of these require script, so legitimate logos generally survive strict sanitization. A few things can still change. External font and image references are removed. Some embedded CSS may be dropped. Text set in a live font will render in a fallback font if that font is unavailable. Logos with text converted to outlines are less affected than logos that depend on fonts. Compare a render of the sanitized file with the original before accepting it, and keep the untouched upload in quarantine if you may need to review it later.

Sanitization also shows that a file ending in .svg is not necessarily editable vector artwork. An SVG can simply wrap a PNG or JPEG in an image element, often as an embedded data URI. That file contains no editable paths, and scaling it degrades the same way a raster does. Look inside the markup: real vector artwork appears as path and shape elements, while a single image element signals an embedded raster. A strict allowlist may also strip embedded images, so decide whether your policy rejects such files or flags them for review. Any PNG previews you generate are display copies, not the editable master.

Making a file safe to display does not settle what you may do with it. Accepting or sanitizing an uploaded logo does not grant permission to modify the artwork, reuse its fonts or use the trademark it represents. Those rights depend on the owner's terms.

Limits of this guidance

No platform-specific evidence was supplied for this article. It rests on generally documented browser and XML parser behavior. It does not draw on testing of particular sanitizer libraries, content management plugins, image services or browser versions. Defaults and edge cases vary between tools and change over time. Confirm the exact behavior of your upload pipeline, sanitizer configuration and serving headers through your own security review or testing before relying on them.