HTML Tutorial 0/139 lessons ~6 min read Lesson 118

    XSS & HTML Injection

    xss & html injection xss and html injection exploit insufficient encoding in html parsing contexts — xss and html

    Course progress0%
    Focus
    18 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    XSS and HTML injection occur when attacker-controlled strings enter HTML parsing contexts — attribute, text, script, URL — and execute as JavaScript in victim browsers. OWASP classifies reflected, stored, and DOM-based XSS; staff engineers map every sink (innerHTML, document.write, inline handlers) in HTML templates and SPA code.

    Business problem

    Stored XSS in profile, comment, or CMS field compromises every viewer's session — wormable on social platforms. Reflected XSS in search URL params targets phishing campaigns. DOM XSS in client routers bypasses server encoding entirely.

    • Impact: Session hijack, keylogging, defacement, crypto mining, BEC pivot.
    • Detection: Bug bounty and DAST often find XSS missed by unit tests.

    Why this feature exists

    HTML merges data and code — browsers don't distinguish attacker text from author markup without encoding. JavaScript execution contexts (script tags, onerror, javascript: href) exist for legitimate apps — attackers abuse insufficient encoding.

    • Reflected: Server echoes input in HTML response.
    • Stored: Input persisted and rendered to others.
    • DOM: Client JS writes untrusted location.hash to innerHTML.

    Browser internals

    Sinks and sources: OWASP DOM XSS wiki lists sources (location, postMessage) and sinks (innerHTML, eval). HTML parser creates script elements from script tags in injected strings unless sanitized.

    • mutation XSS: Legacy browsers re-parse innerHTML assignments — sanitizer must handle mXSS vectors.
    • SVG/MathML: Alternative namespaces in innerHTML enable script in older patterns.

    Rendering workflow

    Safe rendering: Prefer textContent over innerHTML. If HTML needed, sanitize with maintained allowlist (DOMPurify). Template engines auto-escape by default — never disable unless trusted static content.

    • URL context: Encode for href/src; block javascript: and data: except allowlisted MIME.
    • Event handlers: Ban onclick/onerror in HTML — use addEventListener on trusted code only.

    Feature deep dive

    OWASP XSS prevention contexts mapped to HTML:

    • HTML body: Encode < > & " '
    • Attribute: Encode all non-alphanumeric — especially unquoted attrs.
    • JavaScript: JSON.stringify in script blocks — never string concat.
    • URL: encodeURIComponent; validate scheme allowlist http/https/mailto.
    html
    <!-- ❌ Reflected XSS -->
    <p>You searched for: {{ searchQuery }}</p>
    <!-- if searchQuery = <script>alert(1)</script> — game over without encode -->
    <!-- ✅ Encoded -->
    <p>You searched for: {{ encodeHtml(searchQuery) }}</p>
    <!-- ❌ DOM XSS -->
    <script>
    document.getElementById("out").innerHTML = location.hash.slice(1);
    </script>
    <!-- ✅ Safe -->
    <script>
    document.getElementById("out").textContent = decodeURIComponent(location.hash.slice(1));
    </script>

    Accessibility analysis

    XSS via aria-label or title attributes — encoded as attribute context. Screen reader users equally affected by malicious script that manipulates DOM or announces phishing via live regions.

    SEO impact

    Injected links in compromised HTML create spammy outbound links — Google manual action for pure spam. SEO monitoring should alert on unknown external link patterns in crawl.

    Security considerations

    OWASP Top 10 A03 Injection — XSS primary web frontend manifestation. Test with polyglot payloads, event handlers, SVG, template literals in framework bypasses.

    • CSP report-only: Detect injection attempts via violation reports.

    Performance impact

    DOMPurify on every render adds CPU — batch sanitize at save time in CMS not per view. Large innerHTML replacements cause layout thrash — separate perf and security concerns.

    Real production example

    React safe patterns — default JSX escape; danger zone explicit:

    • Lint: eslint-plugin-no-unsanitized for innerHTML assignments.
    html
    // ✅ JSX auto-escapes
    <p>{userBio}</p>
    // ⚠️ Only with sanitize
    import DOMPurify from "dompurify";
    <div dangerouslySetInnerHTML={{
    __html: DOMPurify.sanitize(cmsHtml, { USE_PROFILES: { html: true } })
    }} />

    Enterprise usage

    Bug bounty programs pay critical for stored XSS — internal red team scans quarterly with OWASP ZAP on all form endpoints rendering HTML.

    Common production failures

    Markdown renderer allowed raw HTML passthrough — stored XSS in docs wiki. Angular bypassSecurityTrustHtml misuse on user content. JSON-LD injection breaking out of script string with sequence.

    Architecture review questions

    • Every user/CMS field mapped to encoding context?
    • innerHTML usage inventory — all sanitized or eliminated?
    • Inline event handlers banned in HTML templates?
    • javascript: URLs blocked in link href validation?
    • DOM XSS sources (location, postMessage) audited in SPA?

    Hands-on project

    Project: Fix reflected XSS demo — add encodeHtml, CSP, and ZAP scan proof of fix on search echo page.

    Interview questions

    Difference between reflected, stored, DOM XSS?(Advanced)

    Reflected: server reflects input once in HTML response — often needs victim to click link. Stored: persisted server-side, affects all viewers — highest severity. DOM: entirely client-side sink/source — server encoding doesn't help; fix JS.

    Follow-up: Example DOM XSS in React router?

    Why is innerHTML dangerous but textContent safe?(Intermediate)

    innerHTML invokes HTML parser — attacker string creates active elements and event handlers. textContent treats input as plain text node — no parser execution of markup semantics.

    Follow-up: When is DOMPurify required?

    Escape </script> in JSON-LD script block?(Advanced)

    Breakout via </script> in string closes script block early — escape as \u003c/script or split string concatenation in JS. Better: serialize JSON safely server-side; validate no raw user input in JSON-LD strings.

    Follow-up: Template literal XSS in modern frameworks?

    Try it yourself

    Edit the HTML, CSS, or JS panels — the preview updates as you type.

    Try it yourself

    Preview

    Summary

    XSS and HTML injection exploit insufficient encoding in HTML parsing contexts — OWASP staff mitigations include context-aware escaping, CSP, sanitizer allowlists, and elimination of inline handlers and dangerous sinks.

    Ready to mark this lesson complete?Track your journey across the entire course.