How stored XSS works and why output encoding stops it
stored XSS mechanics, reflected XSS, DOM XSS, script injection via HTML, output context, HTML entity encoding, textContent vs innerHTML
Cross-Site Scripting (XSS)
XSS happens when attacker-controlled data is rendered as executable HTML or JavaScript in another user's browser. The browser has no way to distinguish between your code and the injected code—they both execute in the same origin.
Three XSS Types
Stored XSS: The payload is saved to the database (comment, profile bio, username) and executes for every user who views that data. This is the highest severity because it's persistent and self-spreading.
Reflected XSS: The payload comes from the URL and is reflected directly in the response without storage. Requires the victim to visit a crafted URL.
DOM XSS: The payload never hits the server. JavaScript reads from location.hash, document.referrer, or URL and writes to innerHTML.
Why innerHTML Is Dangerous
// VULNERABLE — innerHTML parses and executes script content
document.getElementById('preview').innerHTML = userInput;
// SAFE — textContent treats input as text, never as HTML
document.getElementById('preview').textContent = userInput;
// Also safe for building elements:
const div = document.createElement('div');
div.textContent = userInput;
container.appendChild(div);Server-Side Output Encoding
const he = require('he');
// In a template engine (non-React):
const safeBio = he.encode(user.bio);
response.send(`${safeBio}
`);
// Even if bio = 'How stored XSS works and why output encoding stops it — Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF) — Web Security Fundamentals for Developers — Script Valley — Script Valley 