2026-08-28 · 8 min read
- html
- security
- xss
Output encoding makes data safe for a particular parser context. HTML text, quoted attributes, URL attributes, CSS declarations, and JavaScript strings are parsed by different grammars, so one universal escape function cannot protect every destination.
Double-encoding happens when already escaped display text is treated as canonical data and escaped again. The safe design stores ordinary text, tracks intentional rich text separately, and applies the correct transformation once at the final output boundary.
Keep canonical data unescaped
Store the name AT&T as those four characters, not AT&T. JSON serializers will escape JSON syntax where needed, and the HTML template should later encode the ampersand for markup. Pre-escaping mixes presentation with data and causes every downstream consumer to guess the current representation.
- Name variables by meaning rather than with vague safe or escaped suffixes.
- Decode legacy entities once during a controlled migration.
- Do not repeatedly decode user input until it stops changing.
- Assert stored values independently from rendered markup.
Match the sink to the context
For browser text, prefer textContent or framework interpolation. For quoted attributes, let the framework encode the value and avoid building markup strings. URLs need scheme and destination validation in addition to attribute encoding because a correctly encoded javascript URL can still be dangerous.
- Avoid innerHTML for ordinary user-provided text.
- Keep event-handler and style attributes out of dynamic templates.
- Use URL constructors and allowlisted schemes for links.
- Treat script data as structured serialization, not string concatenation.
Sanitize intentional rich text
Encoding rich HTML would display its tags as text, while inserting it directly can execute unsafe markup. When the product deliberately accepts rich text, sanitize with a maintained parser and an allowlist covering tags, attributes, URL schemes, and nesting.
Sanitization and encoding solve different problems. Sanitize the rich document according to policy, then place the trusted result only into the intended HTML sink. Keep ordinary comments, names, and labels on the plain-text path.
Test the final DOM and every serialization hop
Use harmless fixtures containing ampersands, quotes, angle brackets, Unicode, entity-looking text, and disallowed URL schemes. Assert visible text, attributes, and DOM structure rather than checking only an intermediate HTML string.
Use Flashman's HTML entities tool to study representations, URL tool for components, JSON formatter for API payloads, diff tool to locate extra transformations, and Markdown viewer for sanitized documentation previews. Browser security tests and framework defaults remain the authority for real application behavior.