← Back to Security

XSS

SecurityMidsecurity

The Question

What is XSS and how do you prevent it?

What a Strong Answer Covers

  • All three types
  • CSP
  • "HttpOnly

Senior-Level Answer

Cross-Site Scripting (XSS) is a code injection vulnerability where an attacker injects malicious scripts into content served to other users. Unlike CSRF, which abuses the browser's trust in a session, XSS abuses the victim's trust in a website — the script runs in the origin of the target site and has full access to its DOM, cookies, and localStorage.

There are three main types:

**Reflected XSS:** Malicious input is immediately echoed back in the HTTP response without sanitization. The payload travels in a URL parameter; the victim is tricked into clicking the link. Common in search pages and error messages.

**Stored XSS:** The payload is persisted in the database (e.g., a comment, profile bio) and rendered for all users who view that resource. Higher severity because it requires no victim-specific link.

**DOM-based XSS:** The vulnerability lives entirely in client-side JavaScript. The page reads from an attacker-controlled source (e.g., `location.hash`, `document.referrer`) and writes it to a dangerous sink (`innerHTML`, `eval`, `document.write`) without sanitization. The server never sees the payload.

Prevention:

**Context-aware output encoding:** The most important defense. HTML-encode untrusted data before inserting it into HTML context (`&` → `&amp;`, `<` → `&lt;`). Use attribute encoding for HTML attributes, JavaScript encoding for script contexts, and URL encoding for URLs. Never mix encoding schemes for the wrong context.

**Use safe APIs:** Prefer `textContent` over `innerHTML`. In React/Vue/Angular, the framework auto-escapes by default — never use `dangerouslySetInnerHTML` or `v-html` with untrusted input. Template engines like Jinja2 auto-escape by default; mark trusted HTML explicitly.

**Content Security Policy (CSP):** A response header (`Content-Security-Policy: default-src 'self'`) that instructs browsers to only execute scripts from allowed sources. A strict CSP with nonces or hashes and no `'unsafe-inline'` is a strong second layer. It does not replace encoding but limits attacker impact even if encoding is missed.

**Input validation:** Validate format and type on input (e.g., reject `<` in a name field), but do not rely on this as the primary defense — encoding at output is more reliable.

**HttpOnly cookies:** Prevent JavaScript from reading session cookies, limiting cookie theft even if XSS fires. Combine with Secure and SameSite flags.

In practice: Django templates auto-escape, React auto-escapes JSX, but both have escape hatches that developers misuse. Code review should flag any raw HTML insertion. A CSP header adds defense-in-depth.

What Separates a 2/3 from a 3/3

2/3 — Passing but Incomplete

Correctly distinguishes at least two XSS types and explains output encoding as the primary defense with a concrete example.

3/3 — Strong Answer

Covers all three XSS types with accurate definitions, explains context-aware encoding, discusses CSP as defense-in-depth, mentions DOM sinks, and references how frameworks handle this.

Common Mistakes

  • Saying 'sanitize input' as the primary fix — output encoding is the correct primary defense
  • Conflating XSS with CSRF — they have different trust directions and mitigations
  • Treating CSP as a complete replacement for encoding rather than a second layer
  • Ignoring DOM-based XSS entirely, which is increasingly common in SPA architectures

Follow-Up Questions

  • Why is output encoding preferred over input sanitization as the primary defense? — Input sanitization removes data that might be needed; encoding preserves data while making it safe in a specific context. Context changes — what's safe for HTML isn't safe for JS.
  • What is a CSP nonce and how does it prevent inline script injection? — A server-generated random value added to the script tag and response header — the browser only executes scripts whose nonce matches.
  • How does a DOM-based XSS attack work if the server never sees the payload? — Fragment identifiers (#hash) and postMessage are never sent to the server. The payload lives in client-side sources and flows to dangerous sinks.
  • How does React's JSX auto-escaping protect against XSS, and where does it break down? — JSX calls React.createElement with string children, which are text nodes — not HTML. Breaks down with dangerouslySetInnerHTML or when rendering to a string server-side without proper care.

Related Questions

  • Authentication vs Authorization
  • JWT — 3 Parts, Signing, Revocation
  • SQL Injection
  • CSRF
  • OAuth 2.0 — Authorization Code Flow

Can You Explain This Cold?

Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.

Get Graded — Free Assessment
GrindQuestionsAITechnical interview assessment
TermsPrivacyAbout