SVG Upload Security: Best Practices for Web Teams

User-submitted Scalable Vector Graphics (SVGs) introduce unique security challenges because they are XML-based files capable of executing JavaScript, fetching external resources, and exploiting XML parsers. Left unchecked, malicious SVG uploads can expose web applications to Cross-Site Scripting (XSS), Server-Side Request Forgery (SSRF), and XML External Entity (XXE) injection attacks. This guide outlines the essential security precautions web development teams must implement to safely accept, process, and serve user-provided SVG illustrations.

1. Sanitize the SVG Content

The most critical defense against malicious SVGs is rigorous sanitization. Because an SVG can contain inline <script> tags, HTML event attributes (such as onload or onerror), and remote links, all incoming files must be scrubbed before storage.

  • Use Proven Sanitization Libraries: Do not rely on custom regular expressions. Use dedicated, well-maintained libraries such as DOMPurify (for JavaScript environments) or specialized backend libraries like svg-sanitizer (PHP) or bleach (Python).
  • Strip Executable Code: Ensure the sanitizer removes <script>, <foreignObject>, <animate>, and <iframe> elements, as well as href attributes pointing to javascript: URIs.
  • Remove Event Handlers: Strip all inline event listeners (e.g., onload, onclick, onmouseover).

2. Protect Against XML Parsing Vulnerabilities (XXE)

Because SVGs are XML documents, the parsers used on the backend are susceptible to XML External Entity (XXE) attacks, which can result in local file disclosure or SSRF.

  • Disable DTDs and External Entities: Configure your XML parser to explicitly disallow Document Type Definitions (DTDs) and external entity resolution.
  • Defend Against Billion Laughs Attacks: Set resource limits on memory and parser depth to prevent denial-of-service (DoS) attacks caused by recursive entity expansion.

3. Serve SVGs Safely

How you deliver SVGs to end users determines the execution context of any payload that might bypass sanitization.

  • Host on an Isolated Domain: Serve user-uploaded media from a separate, sandboxed domain or subdomain (e.g., usercontent.example.com). This ensures that if a script does execute, it cannot access session tokens, cookies, or localStorage tied to your primary application domain.
  • Serve as an Image: Render SVGs using the HTML <img> tag or CSS background properties instead of embedding them inline. Browsers automatically disable JavaScript execution when an SVG is loaded inside an <img> tag.
  • Force Download for Direct Access: If users must open the raw SVG file via a direct link, force the browser to download it rather than render it in the browser tab. Set the HTTP header: Content-Disposition: attachment; filename="illustration.svg"
  • Set the Correct MIME Type: Always return Content-Type: image/svg+xml. Do not return generic types like text/html or text/plain, which might trigger browser content sniffing.

4. Implement Strong HTTP Headers

HTTP response headers provide an extra layer of defense should an SVG bypass initial validation.

  • Content Security Policy (CSP): Apply a strict CSP header to the domain serving the assets: Content-Security-Policy: default-src 'none'; style-src 'self' 'unsafe-inline'; sandbox The sandbox directive disables script execution entirely for documents rendered in that browsing context.
  • Prevent MIME Sniffing: Use X-Content-Type-Options: nosniff to instruct browsers to follow the declared MIME type strictly.

5. Convert SVGs to Raster Formats When Vector Scaling Is Unnecessary

If dynamic scaling and infinite resolution are not strictly required for the user experience, the safest approach is to rasterize the uploaded SVG into a static image format (such as PNG or WebP) upon upload.

By rendering the SVG on a sandboxed backend process and saving only the resulting pixel data, you permanently neutralize all script vectors, external entity calls, and XML parsing risks before the file ever reaches other users.