vuln-class-xss
Cross-Site Scripting (XSS), in plain English
XSS lets an attacker run their own JavaScript in another visitor's browser session on your site. From there: account takeover, stolen passwords, fake login screens.
What this is
Cross-Site Scripting (XSS) is a category of website bug where text controlled by an attacker ends up displayed on your page in a way the browser interprets as code rather than as content. The text becomes JavaScript, the JavaScript runs inside your visitor’s browser, and from your visitor’s perspective the code is part of your site — same domain in the address bar, same lock icon. The attacker gets to do anything your visitor’s logged-in session can do.
The mistake behind every XSS bug is the same: input arrived from somewhere and got written into the page without being properly treated for the place where it ended up. The variations differ only in where the bad input came from.
Three flavours
Reflected XSS
The attacker crafts a URL with the malicious payload in it, sends the URL to a victim (email, chat, ad), and the application echoes the payload back into the response page. Search forms and error messages are common sources. Reach is one victim per click — but that’s enough for targeted phishing.
Stored XSS
The malicious payload is written into the application’s database and served back to anyone who later views the affected page. Forum posts, comments, usernames, profile bios, support-ticket messages, product reviews — any field where one user types text that other users later see. One attack, many victims.
DOM-based XSS
The bug is purely in your frontend code: a JavaScript file in the
page reads attacker-controlled input from somewhere
(location.hash, postMessage, the URL query string) and inserts
it into the page in a way that runs script. Your server may be
entirely innocent — the bug lives in the client-side code.
Why it matters
XSS gives the attacker the visitor’s session at the moment of execution. The concrete consequences:
- Account takeover. Read every cookie not specifically marked off-limits to JavaScript. Even cookies that are marked off-limits don’t fully protect — the script can simply submit requests on the user’s behalf inside the open session.
- Form-data theft. Hook every form submission, exfiltrate every password, payment detail, or message before the form reaches your server.
- Phishing in place. Replace the page contents with a fake login
screen that looks identical to your real one. The address bar
still reads
your-domain.example; the lock is still green. - Worm-style spread. A stored XSS in a social feature can post itself everywhere the victim has access — see Samy on MySpace and the Twitter retweet worms.
In a CMS or admin panel, XSS in an admin’s session is, in practical terms, full administrative control of the site.
How it gets fixed
XSS is fixed by encoding the output for where it lands, not by filtering the input. The same string is safe in one context and dangerous in another.
| Output context | What’s needed |
|---|---|
| HTML body text | Replace <, >, &, ", ' with their HTML-safe equivalents |
| HTML attribute value | HTML-encode and quote the attribute |
| JavaScript string literal | JS-escape, or better, JSON-encode |
| URL parameter | URL-encode the value |
| CSS context | Avoid; if unavoidable, strict allowlist |
In practice, modern frameworks do the right thing by default:
- React, Vue, Svelte, Angular automatically escape text in
templates. The dangerous functions are explicitly named
(
dangerouslySetInnerHTML,v-html,[innerHTML]) so an audit can find them. - Templating engines (Jinja, Twig, ERB, Liquid) escape by
default; the risky escape hatch is
{{ raw }}/safe/ similar. - Backend code that writes HTML by hand is the highest-risk
area; every
out.write("<div>" + x + "</div>")is a potential XSS waiting for the right input.
Layered defences for when something slips through anyway:
- A Content Security Policy with nonces or hashes — see our CSP article. With CSP in place, a successful XSS payload still struggles to load attacker- controlled scripts, exfiltrate to attacker-controlled hosts, or open new framed UI. It doesn’t stop XSS from existing, but it caps the damage.
- HttpOnly + Secure + SameSite cookies for sessions. A session cookie with these attributes can’t be read by JavaScript and isn’t sent on cross-site requests in most cases. Doesn’t cure XSS but reduces its leverage.
How blueredix surfaces XSS
The scanner doesn’t actively inject XSS payloads against your live application — that’s reserved for authorised testing. What we flag:
- CVEs in third-party software (admin panels, CMSs, plugins) with XSS in the description.
- JavaScript libraries with documented XSS bugs and a fixed version available.
- Header tells: missing
Content-Security-Policy, missingX-Content-Type-Options: nosniff, cookies missing theHttpOnlyflag — each of which makes XSS easier to exploit.
A real test of your own code’s XSS resistance is a manual review or a penetration test, not an automated black-box scan.