SVG (Scalable Vector Graphics) is loved by developers and designers for its scalability, small file size, and crisp rendering. But behind its innocent XML-based appearance lies one of the most powerful vectors for Cross-Site Scripting (XSS) attacks.
In this post, we’ll explore how SVGs can be weaponized, real-world risks, and most importantly - how to defend against them.
Why SVG is a Security Nightmare
Unlike regular image formats (JPG, PNG, GIF), SVG is not just an image - it’s a markup language that can contain:
JavaScript
Event handlers (
onload,onbegin, etc.)Embedded HTML
CSS animations
Foreign objects
This flexibility makes SVG extremely powerful for legitimate use cases, but also incredibly dangerous when users can upload or render untrusted SVGs.
Common SVG XSS Attack Vectors
Here are the most effective payloads attackers use:
1. Classic onload Payload
<svg onload="alert('XSS')"></svg>
2. Script Tag Inside SVG
<svg><script>alert('XSS')</script></svg>
3. Animation-based (bypasses many filters)
<svg><animate onbegin="alert('XSS')" attributeName="x" dur="1s" repeatCount="indefinite"/>
4. ForeignObject Trick (very stealthy)
<svg>
<foreignObject>
<body>
<script>alert('XSS')</script>
</body>
</foreignObject>
</svg>
5. Data URI + HTML Injection
<svg><desc><![CDATA[</desc><script>alert('XSS')</script>]]></svg>
Real-World Impact
Many popular platforms (including some CMS, forums, and SaaS tools) have been vulnerable to SVG-based stored XSS.
Attackers can steal cookies, session tokens, perform account takeovers, keylogging, or mine cryptocurrency using Coinhive-style scripts inside SVGs.
Even avatar uploads have been abused when applications serve SVGs with
Content-Type: image/svg+xmlwithout proper sanitization.
Why Many Sanitizers Fail
Most HTML sanitizers (like DOMPurify in default config, or basic PHP filters) don’t fully protect against SVG attacks unless specifically configured for it.
Common mistakes:
Only removing
<script>tagsAllowing
data:URIsNot disabling event handlers
Serving SVGs with executable MIME types
Best Practices to Secure SVG Usage
Never trust user-uploaded SVGs
Use a strict SVG sanitizer like DOMPurify with proper config:
const cleanSVG = DOMPurify.sanitize(userSVG, { USE_PROFILES: { svg: true, svgFilters: true }, FORBID_TAGS: ['script', 'style', 'foreignObject'], FORBID_ATTR: ['onload', 'onerror', 'onbegin'], });Convert SVGs to Safe Formats
Rasterize SVGs to PNG on the server side (using libraries like
sharp,librsvg, orImageMagick).
Serve SVGs with Strict Headers
Use
Content-Disposition: attachmentorContent-Security-Policyto block script execution.Consider
sandboxattribute if embedding.
Use a Dedicated Sanitization Library
svg-sanitizer (PHP)
DOMPurify (JavaScript)
svgcleaner or custom server-side whitelisting
Additional Defenses
Implement Content Security Policy (CSP) with strict
script-srcValidate file signatures (magic bytes) before processing
Limit SVG complexity (size, elements count)
Conclusion
SVG is an incredibly useful format, but treating it like a regular image is a dangerous mistake. Every application that accepts user-controlled SVGs should treat them as potential executable code.
Security rule of thumb:
If your sanitizer doesn’t explicitly support SVG safely - assume it’s vulnerable.


