Identify the XSS vectors specific to markup and deploy HTML's native defenses: sandbox, noopener and Content Security Policy.
Open this lesson in KodokonThe escaping context makes all the difference: a value that is harmless in a text node becomes executable in an attribute. The unquoted attribute is the worst case: a single space in the injected data is enough to create a new attribute, for example an onmouseover handler. Even when properly quoted, a value remains dangerous in sensitive sinks: href and src accept the javascript: scheme, formaction hijacks a form's submission, and srcdoc interprets its entities as HTML once decoded.
<!-- userName = x onmouseover=alert(1) -->
<img src="avatar.png" alt=x onmouseover=alert(1)>
<!-- The unquoted attribute lets a handler in -->An iframe's sandbox attribute strips all privileges, and you then grant them back one by one: allow-scripts, allow-forms, allow-popups... Without allow-same-origin, the embedded document receives an opaque origin: no cookies, no storage, no access to the parent DOM - even if it is served from your own domain.
<iframe
src="https://widget.example.com"
sandbox="allow-scripts allow-forms"
referrerpolicy="no-referrer">
</iframe>A target=_blank link historically gave the opened page a window.opener reference to your tab: it could redirect your page to a phishing clone, the attack known as tabnabbing. Modern browsers now apply noopener implicitly on target=_blank, but spelling it out remains essential for window.open in JavaScript and to document the intent; add noreferrer to also suppress the Referer header.
<a href="https://external.example"
target="_blank"
rel="noopener noreferrer">
View the external resource
</a>A Content Security Policy restricts the sources of executable code and neutralizes most XSS, even when the injection has succeeded. Declared through a <meta http-equiv> tag, it nevertheless has blind spots: the frame-ancestors, report-uri and sandbox directives are ignored there and require the HTTP header. Prefer nonces regenerated on every response over domain allowlists, which the strict-dynamic directive makes obsolete anyway.
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'nonce-r4nd0m'">allow-scripts and allow-same-origin on a same-origin iframe cancel the sandbox?rel=noopener protect against?window.opener to redirect your tab to a phishing site<meta http-equiv>?