Information Security 7 min read

Preventing XSS Attacks in Rich Text Editors: Concepts, Types, and Implementation

This article explains what XSS attacks are, describes the three main XSS types, and provides practical JavaScript functions for sanitizing and restoring rich‑text content to protect web applications from high‑risk cross‑site scripting vulnerabilities.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Preventing XSS Attacks in Rich Text Editors: Concepts, Types, and Implementation

Introduction – Many front‑end projects use rich‑text editors, but they are often vulnerable to Cross‑Site Scripting (XSS) attacks. The author discovered that unfiltered rich‑text input in a recent project was flagged as a high‑severity XSS issue by the internal security team.

What is XSS? – XSS (Cross‑Site Scripting) is a common web security flaw where an attacker injects malicious scripts into a page that is viewed by other users. It occurs when a web application fails to properly filter user input, leading to script execution that can steal data, alter pages, or spread malware.

Three Main Types of XSS

Stored XSS (Stored XSS)

Malicious scripts are saved on the server (e.g., in a blog comment) and served to other users when they request the page.

Reflected XSS (Reflected XSS)

The script is delivered via a URL parameter or input field and executed immediately in the victim’s browser.

DOM‑based XSS (DOM‑based XSS)

The attack manipulates the Document Object Model on the client side using JavaScript, without involving server‑side code.

Rich‑text editors typically fall into the Stored XSS category because the malicious markup is saved in the database and rendered later.

How to Prevent XSS in Rich Text

Simple escaping of dangerous characters is often sufficient. When no sanitization is applied, the submitted rich‑text may contain tags like <script> or attributes such as src , which can execute scripts.

By converting characters such as < , > , & , " , ' , and \ to their HTML entities before storing, the risk is mitigated.

Below is a JavaScript utility that escapes these characters:

export const getXssFilter = (value: string): string => {
  // Define a map of characters to HTML entities
  const htmlEntities = {
    '&': '&',
    '<': '<',
    '>': '>',
    '"': '"',
    '\'': ''',
    '\\': '\',
    '|': '|',
    ';': ';',
    '$': '$',
    '%': '%',
    '@': '@',
    '(': '(',
    ')': ')',
    '+': '+',
    '\r': '',
    '\n': '',
    ',': ','
  };
  // Replace all special characters using a regular expression
  let result = value.replace(/[&<>"'\\|;$%@()+,]/g, match => htmlEntities[match] || match);
  return result;
};

After sanitization, the stored content appears escaped, as shown in the following screenshot (omitted). To display the original formatting to users, the escaped content must be restored either on the front‑end or back‑end.

Front‑end restoration example:

// Restore escaped characters
export const setXssFilter = (input) => {
  return input
    .replace(/\|/g, '|')
    .replace(/&/g, '&')
    .replace(/;/g, ';')
    .replace(/\$/g, '$')
    .replace(/%/g, '%')
    .replace(/@/g, '@')
    .replace(/'/g, "'")
    .replace(/"/g, '"')
    .replace(/\\/g, '\\')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/\(/g, '(')
    .replace(/\)/g, ')')
    .replace(/\+/g, '+')
    .replace(/\r/g, '')
    .replace(/\n/g, '')
    .replace(/,/g, ',');
};

However, this generic restoration can re‑introduce XSS risks in plain‑text contexts. A more targeted approach is to escape only the characters and tags that pose a threat (e.g., script , iframe , object , embed , etc.) while leaving other formatting intact.

Enhanced filter example:

export const getXssFilter = (value: string): string => {
  const htmlEntities = {
    '&': '&',
    '\'': ''',
    '\r': '',
    '\n': '',
    'script': 'script',
    'iframe': 'iframe',
    // 'img': 'img',
    'object': 'object',
    'embed': 'embed',
    'on': 'on',
    'javascript': 'javascript',
    'expression': 'expression',
    'video': 'video',
    'audio': 'audio',
    'svg': 'svg',
    'background-image': 'background-image'
  };
  // Escape special characters
  let result = value.replace(/[&<>"'\\|;$%@()+,]/g, match => htmlEntities[match] || match);
  // Additional handling for dangerous keywords
  result = result.replace(/script|iframe|object|embed|on|javascript|expression|background-image/gi, match => htmlEntities[match] || match);
  return result;
};

This selective sanitization ensures that only high‑risk elements are neutralized, allowing the rich‑text formatting to remain visible without needing a full reversal step.

Conclusion

Escaping dangerous characters and selectively filtering risky tags provides a straightforward and effective way to protect rich‑text inputs from XSS attacks while preserving the intended display for end users.

FrontendJavaScriptsecurityXSSRich TextSanitization
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.