Skip to main content
← πŸ“„ HTML & the platformΒ·Module A3 Β· Lesson 20
TaskBuild a comment list that's XSS-safe. <form>: text input name='comment' + submit button. <ul id='comments'></ul> below. JS: on submit, prevent default, read the input value, create a <li>, set li.textContent (NOT innerHTML), append to #comments, reset the input.

XSS-safe rendering: textContent vs innerHTML, on user input

100 XP9 min
Theory

The mistake

You take a comment from a form, then render it into the DOM:

commentList.innerHTML += "<li>" + userInput + "</li>";

If userInput is <img src=x onerror="fetch('/steal?c='+document.cookie)">, the browser runs that script in YOUR origin. Now the attacker reads your users' cookies, makes requests as them, defaces your page.

The fix

const li = document.createElement("li");
li.textContent = userInput;  // browser treats userInput as plain text, escapes everything
commentList.appendChild(li);

textContent (and innerText) write the string as text. No HTML parsed. The literal characters <, >, & are rendered as you-typed-them, never as tag delimiters.

When you NEED HTML rendering

E.g. you want bold inside comments. Options in order of safety:

  1. Restricted Markdown β€” your server converts a small allowlist (**bold**, *italic*, [link](url)) to safe HTML, with link URLs filtered (javascript: blocked, http(s)://relative allowed). Then you can safely use innerHTML on the server-cleaned output.
  2. DOMPurify β€” client-side library that strips dangerous tags/attrs. Useful for rich-text editors. Still: don't pass arbitrary user input to it; pass YOUR known-format markup.
  3. Never pass raw user input to innerHTML β€” even if you "trust" the user; one compromised account becomes everyone's problem.

What this looks like in a comment list

function addComment(text) {
  const li = document.createElement("li");
  li.textContent = text;
  document.getElementById("comments").appendChild(li);
}

Three lines, XSS-safe, accessible by default.

πŸ”’

Sign up to start coding

Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial β€” card required, cancel anytime.

Sign up β€” free trial β†’

First 10 lessons in each track are free. No card needed for those.

← PreviousNext lesson β†’

Get one Python or web tip a day β€” by email

Short, hand-written, no spam. Unsubscribe in one click.