Encode at the final context

Store the most faithful validated value and encode it at output. Pre-escaped database values are hard to reuse and often become double encoded. The template is where the application finally knows whether a value is HTML text, an attribute, a URL component, or JSON.

For ordinary HTML text and quoted attributes, use a maintained templating system with automatic escaping or an explicit encoder configured for the document charset.

Treat URLs and scripts as distinct languages

A URL embedded in an HTML attribute crosses two contexts: build and validate the URL first, then encode it for the attribute. Avoid inserting data directly into script blocks. When data must reach JavaScript, serialize it as JSON with safe flags or expose it through a data endpoint.

Reject dangerous URL schemes rather than attempting to sanitize arbitrary input into a safe URL.

  • Allowlist expected schemes and hosts.
  • Keep event-handler attributes out of templates.
  • Use textContent instead of innerHTML in browser code.
  • Set a Content Security Policy as an additional boundary.

Make unsafe output conspicuous

If the rendering layer supports a raw or safe-markup type, restrict its construction to small reviewed helpers. A generic raw filter turns a local exception into a system-wide bypass.

Test payloads in every output context, including error messages, emails, exported CSV files, and admin interfaces. Internal screens process the same untrusted data and often have broader permissions.

Verification checkpoint

Render representative hostile strings in text, attributes, URLs, JSON, and administrative views, then inspect the resulting DOM rather than relying only on page source.