--- name: xss-prevention description: Prevent Cross-Site Scripting (XSS) attacks through input sanitization, output encoding, and Content Security Policy. Use when handling user-generated content in web applications. --- # XSS Prevention ## Overview Implement comprehensive Cross-Site Scripting (XSS) prevention using input sanitization, output encoding, CSP headers, and secure coding practices. ## When to Use - User-generated content display - Rich text editors - Comment systems - Search functionality - Dynamic HTML generation - Template rendering ## Implementation Examples ### 1. **Node.js XSS Prevention** ```javascript // xss-prevention.js const createDOMPurify = require('dompurify'); const { JSDOM } = require('jsdom'); const he = require('he'); const window = new JSDOM('').window; const DOMPurify = createDOMPurify(window); class XSSPrevention { /** * HTML Entity Encoding - Safest for text content */ static encodeHTML(str) { return he.encode(str, { useNamedReferences: true, encodeEverything: false }); } /** * Sanitize HTML - For rich content */ static sanitizeHTML(dirty) { const config = { ALLOWED_TAGS: [ 'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'img', 'blockquote', 'code' ], ALLOWED_ATTR: [ 'href', 'src', 'alt', 'title', 'class' ], ALLOWED_URI_REGEXP: /^(?:https?|mailto):/i, KEEP_CONTENT: true, RETURN_DOM: false, RETURN_DOM_FRAGMENT: false }; return DOMPurify.sanitize(dirty, config); } /** * Strict sanitization - For untrusted HTML */ static sanitizeStrict(dirty) { return DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong'], ALLOWED_ATTR: [], KEEP_CONTENT: true }); } /** * JavaScript context encoding */ static encodeForJS(str) { return str.replace(/[<>"'&]/g, (char) => { const escape = { '<': '\\x3C', '>': '\\x3E', '"': '\\x22', "'": '\\x27', '&': '\\x26' }; return escape[char]; }); } /** * URL parameter encoding */ static encodeURL(str) { return encodeURIComponent(str); } /** * Attribute context encoding */ static encodeAttribute(str) { return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') .replace(/\//g, '/'); } /** * Validate and sanitize URLs */ static sanitizeURL(url) { try { const parsed = new URL(url); // Only allow safe protocols if (!['http:', 'https:', 'mailto:'].includes(parsed.protocol)) { return ''; } return parsed.href; } catch { return ''; } } /** * Strip all HTML tags */ static stripHTML(str) { return str.replace(/<[^>]*>/g, ''); } /** * React-style JSX escaping */ static escapeForReact(str) { return { __html: DOMPurify.sanitize(str) }; } } // Express middleware function xssProtection(req, res, next) { // Sanitize request body if (req.body) { req.body = sanitizeObject(req.body); } // Sanitize query parameters if (req.query) { req.query = sanitizeObject(req.query); } next(); } function sanitizeObject(obj) { const sanitized = {}; for (const [key, value] of Object.entries(obj)) { if (typeof value === 'string') { sanitized[key] = XSSPrevention.stripHTML(value); } else if (typeof value === 'object' && value !== null) { sanitized[key] = sanitizeObject(value); } else { sanitized[key] = value; } } return sanitized; } // Express example const express = require('express'); const app = express(); app.use(express.json()); app.use(xssProtection); app.post('/api/comments', (req, res) => { const { comment } = req.body; // Additional sanitization for rich content const safeComment = XSSPrevention.sanitizeHTML(comment); // Store in database // db.comments.insert({ content: safeComment }); res.json({ comment: safeComment }); }); module.exports = XSSPrevention; ``` ### 2. **Python XSS Prevention** ```python # xss_prevention.py import html import bleach from urllib.parse import urlparse, quote import re class XSSPrevention: # Allowed HTML tags for rich content ALLOWED_TAGS = [ 'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'blockquote', 'code' ] ALLOWED_ATTRIBUTES = { 'a': ['href', 'title'], 'img': ['src', 'alt'] } @staticmethod def encode_html(text: str) -> str: """HTML entity encoding - safest for text content""" return html.escape(text, quote=True) @staticmethod def sanitize_html(dirty_html: str) -> str: """Sanitize HTML - for rich content""" return bleach.clean( dirty_html, tags=XSSPrevention.ALLOWED_TAGS, attributes=XSSPrevention.ALLOWED_ATTRIBUTES, strip=True ) @staticmethod def sanitize_strict(dirty_html: str) -> str: """Strict sanitization - strip all HTML""" return bleach.clean( dirty_html, tags=[], attributes={}, strip=True ) @staticmethod def strip_html(text: str) -> str: """Remove all HTML tags""" return re.sub(r'<[^>]*>', '', text) @staticmethod def sanitize_url(url: str) -> str: """Validate and sanitize URLs""" try: parsed = urlparse(url) # Only allow safe protocols if parsed.scheme not in ['http', 'https', 'mailto']: return '' return url except: return '' @staticmethod def encode_for_javascript(text: str) -> str: """Encode for JavaScript context""" escape_map = { '<': '\\x3C', '>': '\\x3E', '"': '\\x22', "'": '\\x27', '&': '\\x26', '/': '\\x2F' } return ''.join(escape_map.get(char, char) for char in text) @staticmethod def encode_url_param(text: str) -> str: """Encode for URL parameters""" return quote(text, safe='') # Flask integration from flask import Flask, request, jsonify from functools import wraps app = Flask(__name__) def sanitize_input(f): """Decorator to sanitize all request inputs""" @wraps(f) def decorated_function(*args, **kwargs): if request.is_json: data = request.get_json() request._cached_json = sanitize_dict(data) return f(*args, **kwargs) return decorated_function def sanitize_dict(data: dict) -> dict: """Recursively sanitize dictionary values""" sanitized = {} for key, value in data.items(): if isinstance(value, str): sanitized[key] = XSSPrevention.strip_html(value) elif isinstance(value, dict): sanitized[key] = sanitize_dict(value) elif isinstance(value, list): sanitized[key] = [ sanitize_dict(item) if isinstance(item, dict) else XSSPrevention.strip_html(item) if isinstance(item, str) else item for item in value ] else: sanitized[key] = value return sanitized @app.route('/api/comments', methods=['POST']) @sanitize_input def create_comment(): data = request.get_json() comment = data.get('comment', '') # Additional rich content sanitization safe_comment = XSSPrevention.sanitize_html(comment) return jsonify({'comment': safe_comment}) # Django template filter from django import template from django.utils.safestring import mark_safe register = template.Library() @register.filter(name='sanitize_html') def sanitize_html_filter(value): """Django template filter for HTML sanitization""" sanitized = XSSPrevention.sanitize_html(value) return mark_safe(sanitized) # Usage in templates: # {{ user_content|sanitize_html }} ``` ### 3. **React XSS Prevention** ```javascript // XSSSafeComponent.jsx import React from 'react'; import DOMPurify from 'dompurify'; // Safe text rendering (React automatically escapes) function SafeText({ text }) { return