XSS

What is XSS (Cross-Site Scripting)?

Cross-Site Scripting (XSS) is a class of web vulnerability where an attacker injects malicious scripts into content viewed by other users. When executed in a victim’s browser, these scripts can steal cookies, perform actions on behalf of the user, display fake UI, or exfiltrate sensitive data. XSS is a client-side issue that arises when untrusted data is included in web pages without proper handling.


Types of XSS

  1. Stored (Persistent) XSS
    Malicious input is stored on the server (database, message board, profile) and later served to other users. This is dangerous because every visitor to the affected page may execute the malicious script.

  2. Reflected (Non-Persistent) XSS
    Malicious payloads are reflected off the server in a response (for example, in an error message or search result) and executed when the victim clicks a crafted link.

  3. DOM-Based XSS
    The vulnerability exists entirely in client-side code (JavaScript). An application directly writes untrusted data into the DOM without proper sanitization or encoding.


Why XSS matters (Impact)

  • Session hijacking and account takeover

  • Unauthorized actions (CSRF amplification)

  • Data exfiltration (personal data, tokens)

  • Defacement, phishing and malware distribution

  • Loss of user trust and regulatory consequences


How XSS typically happens (root causes)

  • Inserting user input into HTML context without escaping

  • Using innerHTML, eval, or document.write with untrusted data

  • Incorrect or missing output encoding for the target context (HTML, attribute, JS, CSS, URL)

  • Over-trusting client input or using string concatenation to build markup

  • Misconfigured Content Security Policy or missing security headers


Secure development principles to prevent XSS

Follow the principle: Never trust user input; always treat it as untrusted. Use multiple layers of defense.

1. Contextual output encoding (first and most important)

Always encode/escape data for the context where it will appear:

  • HTML body: escape <, >, &, " and '.

  • Attribute values: ensure quotes and dangerous characters are escaped.

  • JavaScript context: use JS string escaping when injecting into scripts.

  • URL context: use URL encoding for query parameter insertion.

  • CSS context: avoid injecting untrusted data into CSS; if necessary, use strict escaping.

Examples:

PHP (escaping for HTML):

echo htmlspecialchars($username, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

Django templates (autoescaping enabled by default — prefer templates over manual concatenation):

{{ user_input }}

React (avoid raw HTML; React escapes output by default):

// Safe: React escapes by default
<div>{userInput}</div>

// Dangerous: only use if you trust the HTML and sanitize it first
<div dangerouslySetInnerHTML={{ __html: trustedHtml }} />

2. Use safe APIs and templating engines

Prefer frameworks/templates that auto-escape output (e.g., Razor, Django templates, Jinja2 with autoescape). Avoid manually concatenating HTML strings.

3. Sanitize HTML when you must accept rich content

If users submit HTML (WYSIWYG), sanitize on server side with a well-maintained library that removes scripts, event handlers, and unsafe tags while allowing a whitelist of safe tags and attributes.

Libraries:

  • DOMPurify (JavaScript) for client/server sanitization

  • HTML Purifier (PHP)

  • Bleach (Python)

Always sanitize on the server even if you sanitize on client.

4. Avoid dangerous DOM APIs and patterns

  • Do not use innerHTML, outerHTML, document.write, or eval with untrusted input.

  • If you must insert HTML, use safe DOM methods (createElement, textContent, setAttribute) to set text or attributes.

Safe DOM insertion:

const p = document.createElement('p');
p.textContent = userInput; // safe insertion as text
container.appendChild(p);

5. Content Security Policy (CSP)

Use CSP to add another layer of protection. A strict CSP can significantly reduce impact by blocking inline scripts and unauthorized script sources.

Example header (start strict, relax carefully):

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';

Notes:

  • CSP is a mitigation, not a replacement for proper escaping and sanitization.

  • Avoid using 'unsafe-inline' and avoid nonce misuse.

6. HttpOnly and Secure cookies

Mark session cookies with HttpOnly; Secure; SameSite=Strict to reduce risk of cookie theft via XSS.

Example Set-Cookie header:

Set-Cookie: sessionid=abc123; HttpOnly; Secure; SameSite=Strict; Path=/

7. Input validation and output filtering

Validate input shapes and lengths (e.g., expected formats) to reduce attack surface. Validation is not a substitute for output encoding but helps.

8. Principle of least privilege for frontend data

Avoid exposing sensitive data to the client unless necessary. The less sensitive data accessible in the browser, the lower the potential impact of XSS.

9. Security testing and scanning

  • Include XSS tests in your automated test suite (unit, integration).

  • Use security scanners and manual penetration testing to detect DOM XSS and edge cases.

  • Review third-party libraries and dependencies for vulnerabilities.


Practical examples — safe vs unsafe patterns

Unsafe (inserts raw HTML from user):

// Dangerous: vulnerable to DOM XSS if userInput contains HTML/JS
document.getElementById('output').innerHTML = userInput;

Safe (textContent prevents execution):

document.getElementById('output').textContent = userInput;

Unsafe server-side template insertion (no escaping):

// Dangerous: directly prints unescaped input in HTML
echo "<div>$comment</div>";

Safe server-side with escaping:

echo '<div>' . htmlspecialchars($comment, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</div>';

When allowing sanitized HTML (example using DOMPurify on the server or client):

const clean = DOMPurify.sanitize(userProvidedHtml);
container.innerHTML = clean;

Detecting XSS in your app

  • Manual code review: search for innerHTML, eval, document.write, string concatenation into markup, use of dangerouslySetInnerHTML.

  • Automated static analysis and SAST tools can find risky patterns.

  • Dynamic scanning (DAST) and penetration testing help find stored/reflected XSS.

  • Logging unexpected script tags or suspicious input patterns in server logs for investigation.


Developer checklist before release

  • All dynamic data is encoded for the correct context.

  • Framework’s autoescape is enabled and used consistently.

  • No use of unsafe DOM APIs with untrusted data.

  • HTML inputs are sanitized server-side with a trusted library.

  • CSP header is set and reviewed for effectiveness.

  • Session cookies set with HttpOnly, Secure and SameSite attributes.

  • Testing includes XSS cases (unit and integration).

  • Third-party HTML or widgets are sandboxed or sanitized.

  • Security review / pentest performed for public-facing forms and message boards.


Common FAQs

Q: Is client-side sanitization enough?
A: No. Client-side sanitization can be bypassed by attackers. Always sanitize and validate on the server.

Q: Should I block all HTML input from users?
A: If you do not need HTML, block it and treat input as plain text. If HTML is required, use strict server-side sanitization and a whitelist approach.

Q: Does CSP eliminate the need for escaping?
A: No. CSP is a valuable defense-in-depth control but does not replace proper contextual encoding and sanitization.


Quick reference (best practices summary)

  • Encode output for the context (HTML, attribute, JS, CSS, URL).

  • Use framework autoescaping and safe templating.

  • Sanitize rich HTML server-side with a trusted library.

  • Avoid innerHTML, eval, and other dangerous APIs.

  • Implement a strict Content Security Policy.

  • Protect cookies with HttpOnly, Secure, and SameSite.

  • Test proactively and include XSS checks in CI.


Conclusion

XSS remains one of the most common web vulnerabilities, but it is preventable. The reliable approach combines contextual output encoding, server-side sanitization when HTML is allowed, safe DOM practices, secure cookie attributes, and a well-configured Content Security Policy. Treat XSS prevention as a core part of the development lifecycle — build safe defaults into templates, libraries, and team workflows to keep applications and users secure.

HOME LEARN COMMUNITY DASHBOARD