XSS

Overview

Cross-Site Scripting lets an attacker inject script into a page viewed by another user. Because that script executes in the context of the trusted site, it can bypass the protection users normally expect from the Same-Origin Policy and interact with page content, session state, and browser-side functionality as if it belonged there. These notes cover the XSS model, the three main types, practical impact, and safe output handling patterns across common stacks.

Why XSS Matters

XSS runs in the victim’s browser under the target origin. That is what makes it powerful. The browser treats the injected code as part of the trusted site rather than as a foreign page.

Typical impact includes:

Same-Origin Policy Context

The Same-Origin Policy separates data by protocol, hostname, and port. Normally, one origin should not be able to read or manipulate another origin’s sensitive state. XSS is dangerous because the malicious script does not come from a different origin anymore; it executes inside the vulnerable origin itself.

Basic JavaScript For Testing

For simple verification and payload adaptation, a few browser-side functions are enough:

Practical point: different browsers sometimes behave differently around parsing quirks, so payloads should be validated in a realistic browser context.

Types of XSS

Reflected XSS

The payload comes from the request and is reflected immediately in the response. Search pages, error messages, and filters are common targets.

Stored XSS

The payload is stored by the application first and then rendered to other users later. Comments, reviews, chat messages, and profile fields are common storage points.

DOM-Based XSS

The vulnerability lives in client-side JavaScript. The payload may never be stored server-side or reflected directly by the backend; instead, frontend code reads untrusted data and inserts it unsafely into the DOM.

What Makes XSS Possible

Reflected XSS Pattern

The classic reflected case is a search result page that echoes a query parameter straight into HTML.

$search_query = $_GET['q'];
echo "<p>You searched for: $search_query</p>";

If the parameter is inserted without escaping, a payload such as the following may execute:

<script>alert(document.cookie)</script>

The fix is output encoding in the right context, not just “remove bad words”.

Stored XSS Pattern

Stored XSS usually looks like a comment or review system that saves user input and later renders it to others without escaping.

$comment = $_POST['comment'];
mysqli_query($conn, "INSERT INTO comments (comment) VALUES ('$comment')");

$result = mysqli_query($conn, "SELECT comment FROM comments");
while ($row = mysqli_fetch_assoc($result)) {
    echo $row['comment'];
}

Once that content is rendered to another user, the attacker’s script executes in their browser instead of the attacker’s own session.

DOM-Based XSS Mindset

DOM XSS appears when frontend code reads attacker-controlled values from places like location.search, location.hash, document.referrer, or postMessage data and inserts them into dangerous sinks such as innerHTML or document.write.

The backend may be perfectly clean in that case. The browser-side script is the vulnerable component.

Safe Output Handling By Stack

PHP

$escaped_search_query = htmlspecialchars($search_query);
echo "<p>You searched for: $escaped_search_query</p>";

Node.js

const sanitizeHtml = require('sanitize-html');
const sanitizedSearchTerm = sanitizeHtml(searchTerm);

Python / Flask

from html import escape
escaped_query = escape(query)

ASP.NET

var encodedInput = HttpUtility.HtmlEncode(userInput);

The exact function varies, but the principle is stable: encode for the correct output context before rendering.

Testing Workflow

What To Look For During Review

Mitigation

Key Takeaways