# What is cross-site scripting (XSS)?

A browser can't tell a stranger's words from the site's own code unless the site marks them as text.

Cross-site scripting, or XSS, happens when a site takes something a user typed and puts it into a page in a way that lets the browser read it as instructions instead of words. If those instructions include JavaScript, it can run in the browser of whoever opens that page.

The examples below use a made-up guestbook. Typing a tag into a real site's comment box is testing that site, even if it's only bold, and other people may see it. Get permission before you access anything, stay in the practice labs, and never touch a real site. The [ethics agreement](https://bugclubhq.com/ethics) is how BugClub HQ holds students to that.

## A browser can't tell who wrote what

A web page is a long run of text. Some of it is content, like a sentence you read. Some of it is HTML, which tells the browser how to build the page. Angle brackets mark the difference. When the browser reaches `<b>`, it doesn't show those three characters. It treats them as an instruction to make what follows bold.

A guestbook has to put what you typed into that same run of text. If it pastes your message straight in, the browser can't tell your words from the site's own HTML.

## What a bold word shows you

A made-up visitor named Alex signs the made-up guestbook with this:

`<b>hello</b>`

A site that handles this properly shows it exactly as typed, brackets and all:

> Alex wrote: `<b>hello</b>`

A site with the bug shows this:

> Alex wrote: **hello**

The brackets are gone because the browser took them as an instruction. Nothing ran, because a bold tag only changes how a word looks. But a stranger's text got to give the browser instructions. In a practice lab, that's enough proof, and you stop there.

## What actually runs, and where

HTML describes a page. JavaScript is a program, and browsers run programs. The hole that lets in a bold tag can let in JavaScript too, unless something else blocks it. Strictly speaking, a bold word on its own is called HTML injection. It's XSS when JavaScript can run.

That JavaScript runs in the viewer's browser. The server only stores and sends text. The browser treats it as the site's own code, so it can do what the site's own code can do for that person: read and change the page, and send requests to the site as them if they're signed in.

That's how the MySpace worm spread in October 2005. Samy Kamkar put a script in his own profile, and it copied itself onto the profile of anyone who viewed it. It reached over a million users in about 20 hours. He pleaded guilty in 2007.

## Stored and reflected

The two common kinds differ in where the text lives and who it reaches.

Stored means the site saves it. A guestbook entry, a comment or a bio goes into the database, and the site puts it into the page for everyone who opens that page. Nobody has to do anything except visit. The MySpace worm was this kind.

Reflected means the site doesn't save it. A search page that repeats what you typed sends your words back for that one request. It reaches only the person whose request carried it, so anyone else would have to be tricked into opening a link.

There's a third kind, DOM-based, where the page's own JavaScript writes the text into the page in the browser after it loads. The cause is the same in all three: a user's text went into the page without being marked as text.

## The fix is output encoding

Before a site puts a user's text into a page, it swaps the characters that mean something to the browser for codes that mean "just show this character." `<` becomes `&lt;` and `>` becomes `&gt;`. The browser draws the bracket on screen and never starts a tag, so Alex's message shows up exactly as typed. That's output encoding, and you'll also hear it called escaping.

It happens as the text goes into the page because the right encoding depends on where the text lands. A paragraph, an HTML attribute and a piece of JavaScript all follow different rules.

Most modern web frameworks do this for you by default. The bug tends to show up where a developer switches that off or builds the HTML by hand. If a site really wants people to post formatting, it needs a sanitizer, a tested library that keeps a short list of safe tags and removes the rest.

Filtering what people type doesn't replace encoding. Neither does a Content Security Policy, a rule that tells the browser which scripts it may run, though it's a useful backup if something slips through.

Week 4 of the [curriculum](https://bugclubhq.com/curriculum) covers this on a practice guestbook, and the proof stops at something harmless.

- [All guides](https://bugclubhq.com/learn)
- [The ethics agreement](https://bugclubhq.com/ethics)
