DEV Community

Cover image for 💥 How an SVG Can Break Your React /Next.js App and How to Defend It
Vladimir Kukresh
Vladimir Kukresh

Posted on • Edited on

💥 How an SVG Can Break Your React /Next.js App and How to Defend It

How an SVG Can Break Your React /Next.js App and How to Defend It

🧨 Real-World Scenario

You're building a profile page. A user uploads their avatar — maybe even in .svg format. Everything works smoothly. But then...

  • Another user visits their profile.
  • Their browser loads the avatar.
  • Suddenly, JavaScript executes in the background.
  • Session hijacked. CSRF triggered. Game over.

How?

Because you let someone upload an SVG — and forgot how dangerous it can be.


🕳️ What Is Stored XSS via File Upload?

Stored XSS happens when an attacker injects JavaScript into a system (e.g., via form or upload), and this code is stored on the server and later rendered in other users’ browsers.

With SVGs, it’s even sneakier:

<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert(document.cookie)</script>
</svg>
Enter fullscreen mode Exit fullscreen mode

If your backend doesn’t sanitize or serve this file safely, it will happily deliver this payload to unsuspecting users.


⚛ Why React / Next.js Apps Are Not Immune

React protects you from XSS inside JSX, but not from files served by your backend.

That means:

  • If your API accepts and serves SVGs as-is,
  • And you render them via <img src={user.avatarUrl} />...

You're trusting the browser not to execute embedded scripts. Bad idea.


🔐 How to Prevent Stored XSS in Your React / Next.js App

1. ❌ Don't Allow SVG Uploads (Unless Absolutely Necessary)

SVGs support:

  • <script>, onload, onmouseover
  • <foreignObject> (HTML inside SVG)
  • External scripts via <use xlink:href="...">

If you don’t sanitize, disallow them entirely.


2. ✅ If You Allow SVGs, Serve Them Safely

Never render them inline. Always serve with headers like:

Content-Type: image/svg+xml
Content-Disposition: attachment; filename="avatar.svg"
Enter fullscreen mode Exit fullscreen mode

This tells the browser: “Download this. Don’t render inline.”

In Next.js (API route or middleware):

res.setHeader('Content-Disposition', 'attachment');
Enter fullscreen mode Exit fullscreen mode

Or: Convert the SVG to PNG server-side and serve that instead.


3. 🧽 Sanitize SVGs (If You Really Must)

Use tools like:

⚠️ Never trust the file extension. Inspect and clean content.


4. 🛡 Add a Strict Content Security Policy (CSP)

Add this via next.config.js:

module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: `
              default-src 'self';
              script-src 'self';
              img-src 'self' data:;
              object-src 'none';
              base-uri 'none';
            `.replace(/\s{2,}/g, ' ').trim(),
          },
        ],
      },
    ];
  },
};
Enter fullscreen mode Exit fullscreen mode

This prevents:

  • Inline scripts,
  • Loading SVGs via <object>,
  • Abuse of <base> tags.

🧠 Developer Checklist

What to Do
🔍 Inspect file content, not just extensions
📛 Block dangerous MIME types or force download
🧼 Sanitize SVGs or disallow them
🧱 Add CSP headers
🚫 Never use dangerouslySetInnerHTML with user content

🧵 TL;DR

SVGs aren’t "just images" — they’re XML with scripting capabilities.

If you're not validating uploads and locking down how they're served, you're one SVG away from stored XSS.


💬 Let’s Discuss

Have you dealt with SVG-related vulnerabilities in your frontend apps?

Do you allow SVGs in uploads, or ban them entirely?

Drop your thoughts, horror stories, or tips in the comments 👇


🙌 Follow for More

I'm writing more about:

  • Frontend security for React/Next.js devs
  • Real-world web exploit mitigation
  • CSP, SSRF, XSS, and modern browser defences

Follow me here on LinkedIn

Top comments (0)