HTML Entity Encoding for XSS Protection
What is XSS?
Cross-Site Scripting (XSS) occurs when an attacker injects malicious scripts into web pages viewed by other users. HTML entity encoding is the first line of defense — it converts special characters into their entity equivalents so browsers treat them as text, not executable code.
<script>alert('XSS')</script>
<!-- After encoding, it renders as literal text -->
<script>alert('XSS')</script>
Critical Characters to Encode
| Character | Entity | Numeric | Why It's Dangerous |
|---|---|---|---|
< | < | < | Opens HTML tags |
> | > | > | Closes HTML tags |
& | & | & | Starts entity references |
" | " | " | Breaks out of attributes |
' | ' | ' | Breaks single-quoted attributes |
/ | / | / | Can close script tags |
innerHTML vs textContent
The most common XSS vector in JavaScript is using innerHTML with untrusted input. textContent is always safe because it never parses HTML.
// DANGEROUS: renders HTML, allows XSS
const userInput = '<img src=x onerror=alert(1)>';
element.innerHTML = userInput; // executes script!
// SAFE: treats everything as plain text
element.textContent = userInput; // displays literal text
// Manual encoding for innerHTML
function escapeHtml(str) {
const map = { '&': '&', '<': '<', '>': '>',
'"': '"', "'": ''' };
return str.replace(/[&<>"']/g, c => map[c]);
}
element.innerHTML = escapeHtml(userInput); // safe
Rule: Never use innerHTML with user input unless you encode it first. Use textContent for plain text. If you must render HTML, sanitize it with a library like DOMPurify.
Framework Auto-Encoding
Modern frameworks encode output automatically. React, Vue, and Angular all escape interpolation by default, but certain patterns bypass this protection.
// React: safe by default
function Comment({ text }) {
return <div>{text}</div> // auto-encoded
}
// React: DANGEROUS - bypasses encoding
function UnsafeComment({ html }) {
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}
// Vue: safe by default
<template>
<div>{{ userText }}</div> <!-- auto-encoded -->
</template>
// Vue: DANGEROUS - raw HTML
<div v-html="userText"></div>
// Server-side: always encode before template insertion
// Python
from markupsafe import escape
safe_name = escape(user_input)
// Node.js
const safe = require('html-entities').encode(userInput);
Need to encode or decode HTML entities?
Try HTML Entity Encoder/Decoder