Security

Updated 8 April 2026

Security best practices for using React Advanced Rich Text Editor

Security is a shared responsibility between the editor and your backend. This guide details the protections that ship out of the box—DOMPurify sanitization, tag allowlists, safe paste—and shows you how to layer additional defenses like server-side validation, CSP headers, storage policies, and threat modeling. Use it as a checklist when launching new surfaces or reviewing compliance requirements.

Overview

The editor includes built-in security features to protect against XSS attacks and other security vulnerabilities. However, proper implementation and additional server-side validation are essential.

  • Client guardrails: DOMPurify sanitizes every change, copy/paste is scrubbed, and toolbar options honor the allowedTags whitelist.
  • Server responsibilities: Re-sanitize, validate URLs/hosts, and enforce storage rules because browsers can be bypassed.
  • Defense in depth: Combine CSP headers, iframe sandboxing (if embedding), rate limits, and security logging for high-risk deployments.

Built-in Security Features

1. DOMPurify Integration

All HTML content is automatically sanitized using DOMPurify, which:

  • Removes potentially dangerous HTML
  • Sanitizes attributes
  • Prevents XSS attacks
  • Cleans pasted content from external sources
// Content is automatically sanitized
<Editor value={content} onChange={setContent} />

DOMPurify runs in STRICT mode with a curated whitelist. If you add custom plugins that inject new attributes (e.g., tracking IDs), extend the sanitizer config on both client and server so content stays consistent.

2. Allowed Tags Control

Restrict which HTML tags can be used in the editor:

// Only allow safe tags
<Editor
  value={content}
  onChange={setContent}
  allowedTags={[           // Restrict allowed HTML tags
    'p', 'h1', 'h2', 'h3',
    'strong', 'em', 'u',
    'ul', 'ol', 'li',
    'a', 'img'
  ]}
/>

Use granular lists per surface. Comments might only allow inline tags, while knowledge bases can include tables and media. Keep the lists in shared constants so client and server stay aligned.

3. Safe Paste

Pasted content is automatically cleaned:

  • Removes dangerous scripts
  • Sanitizes attributes
  • Cleans Word document formatting
  • Removes event handlers

Smart quotes are normalized, tracking pixels removed, and nested spans collapsed. For strict environments hook into onPaste to block external images or rewrite URLs before they land in state.

Security Best Practices

1. Server-Side Validation

Always validate and sanitize content on the server before storing or displaying:

// Server-side example (Node.js)
const DOMPurify = require('isomorphic-dompurify');

app.post('/api/content', (req, res) => {
  const { content } = req.body;  // Get HTML content from request
  
  // Sanitize on server - CRITICAL for security
  const sanitized = DOMPurify.sanitize(content, {
    ALLOWED_TAGS: ['p', 'h1', 'h2', 'strong', 'em'],  // Whitelist allowed tags
    ALLOWED_ATTR: ['href', 'src', 'alt']               // Whitelist allowed attributes
  });
  
  // Store sanitized content in database
  saveToDatabase(sanitized);
});

2. Restrict Allowed Tags

Use the allowedTags prop to limit HTML tags based on your use case:

// Minimal tags for simple text
<Editor
  allowedTags={['p', 'strong', 'em', 'br']}
/>

// Rich content with images and links
<Editor
  allowedTags={[
    'p', 'h1', 'h2', 'h3',
    'strong', 'em', 'u',
    'ul', 'ol', 'li',
    'a', 'img', 'table',
    'thead', 'tbody', 'tr', 'th', 'td'
  ]}
/>

3. Content Security Policy (CSP)

Implement Content Security Policy headers:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';

Tighten the policy for production by whitelisting only critical CDNs (fonts, icons) and—if policy allows—replacing 'unsafe-inline' with nonce-based scripts or 'strict-dynamic'.

4. Validate on Display

When displaying editor content, always use React’s dangerouslySetInnerHTML carefully:

// ✅ Good: Content is already sanitized by editor
<div dangerouslySetInnerHTML={{ __html: content }} />

// ⚠️ Better: Sanitize again before display
import DOMPurify from 'dompurify';

<div dangerouslySetInnerHTML={{ 
  __html: DOMPurify.sanitize(content) 
}} />

5. Disable Auto-Save for Sensitive Content

For sensitive content, disable localStorage auto-save:

<Editor
  storageKey=""  // Disable auto-save
  value={content}
  onChange={setContent}
/>

Also consider encrypting drafts or falling back to sessionStorage when compliance rules demand purging content once the tab closes.

Common Security Risks

1. XSS (Cross-Site Scripting)

Risk: Malicious scripts injected into content

Protection:

  • DOMPurify automatically removes scripts
  • Allowed tags restriction prevents script tags
  • Server-side validation required
  • Encode user HTML before forwarding it to third-party systems to avoid double-decoding attacks

2. HTML Injection

Risk: Unwanted HTML elements

Protection:

  • Use allowedTags prop
  • Server-side tag whitelisting
  • Regular content validation
  • Rewrite unsupported attributes (e.g., inline event handlers) before persisting

Risk: Malicious links in content

Protection:

  • Validate URLs on server
  • Use rel="noopener noreferrer" (automatically added)
  • Check link destinations
  • Add analytics or warning banners for external domains in regulated environments

Security Checklist

  • Use allowedTags to restrict HTML tags
  • Sanitize content on the server before storing
  • Validate content before displaying
  • Implement Content Security Policy
  • Disable auto-save for sensitive content
  • Validate URLs in links and images
  • Regular security audits
  • Keep dependencies updated
  • Monitor for security vulnerabilities
  • Test with malicious input
  • Log editing actions (create/update/delete) when compliance requires audit trails
  • Back up sanitized content and optionally store raw submissions for forensic review

Example: Secure Implementation

import { useState } from 'react';
import Editor from '@webbycrown/react-advanced-richtext-editor';
import DOMPurify from 'dompurify';

function SecureEditor() {
  const [content, setContent] = useState('');

  // Sanitize before saving
  const handleSave = async () => {
    const sanitized = DOMPurify.sanitize(content, {
      ALLOWED_TAGS: ['p', 'h1', 'h2', 'strong', 'em', 'ul', 'ol', 'li'],
      ALLOWED_ATTR: []
    });
    
    // Send to server
    await fetch('/api/content', {
      method: 'POST',
      body: JSON.stringify({ content: sanitized })
    });
  };

  return (
    <>
      <Editor
        value={content}
        onChange={setContent}
        allowedTags={['p', 'h1', 'h2', 'strong', 'em', 'ul', 'ol', 'li']}
        storageKey=""  // Disable auto-save
      />
      <button onClick={handleSave}>Save</button>
    </>
  );
}

Pattern recap: sanitize at every boundary (client before send, server before store, client before render) and keep allowedTags synchronized so content isn’t stripped unexpectedly.