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) orbleach(Python). - Strip Executable Code: Ensure the sanitizer removes
<script>,<foreignObject>,<animate>, and<iframe>elements, as well ashrefattributes pointing tojavascript: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 liketext/htmlortext/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'; sandboxThesandboxdirective disables script execution entirely for documents rendered in that browsing context. - Prevent MIME Sniffing: Use
X-Content-Type-Options: nosniffto 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.