Cross-Site Scripting: How One Input Box Can Hijack Your Users
Imagine you run a small online store. A customer leaves a product review that says: "Great shoes!" Harmless. But another visitor leaves a review that, instead of words, contains a snippet of JavaScript. Your site saves it like any other review — and now every single person who views that product page silently runs the attacker's code in their browser, as if it came from you.
That's Cross-Site Scripting, or XSS, and it remains one of the most common and dangerous vulnerabilities on the web. It's been in the OWASP Top 10 for two decades, and it's still everywhere.
The core problem: your site can't tell data from code
A browser trusts whatever your website sends it. If your site takes something a user typed — a comment, a username, a search term, a URL parameter — and puts it back onto the page without cleaning it first, the browser can't tell the difference between your intended content and an attacker's injected script. It just runs it.
There are three flavours worth knowing:
- Stored XSS is the worst. The malicious script is saved in your database (like that fake review) and served to everyone who views the page. One injection, unlimited victims.
- Reflected XSS bounces the script off your server via a crafted link. The attacker sends a victim a URL to your site; your site reflects the payload straight back into the page and it executes. The link looks like it goes to your trusted domain, which is exactly why people click it.
- DOM-based XSS happens entirely in the browser, when your JavaScript takes untrusted input and writes it into the page directly.
What an attacker actually does with it
"Running JavaScript on your page" sounds abstract until you realise what that JavaScript can do. Because it runs as your site, in your user's authenticated session, an attacker can:
- Steal session cookies and log in as the victim — no password needed.
- Read anything on the page: personal data, messages, account details, tokens.
- Perform actions as the user: change their email, transfer funds, make purchases, post content.
- Rewrite the page to show a fake login form and harvest credentials directly.
- Spread, in the stored case, to every future visitor automatically.
From the victim's side, nothing looks wrong. The URL is your real domain. The page is your real page. That's the whole danger: XSS weaponises the trust people place in you.
Why it keeps happening
Modern frameworks like React and Vue actually protect you from a lot of XSS by default — they escape output automatically. So why is it still so common?
Because developers keep finding ways around those protections: using dangerouslySetInnerHTML, injecting raw HTML, building URLs from user input, trusting data from an API that itself trusted user input. And AI-generated code is especially prone to it — an assistant focused on "make this feature work" will happily render user input directly, because that's the shortest path to something that runs.
How to defend against it
There's no single switch, but a few layers stop the vast majority of XSS:
Escape output by default. Whenever you put user-controlled data into a page, encode it for the context it lands in (HTML, attribute, JavaScript, URL). Modern frameworks do this — don't fight them, and be very suspicious of any "render raw HTML" escape hatch.
Never trust input, even from your own API. Data that came from a user is tainted forever, no matter how many systems it passes through.
Deploy a Content-Security-Policy (CSP). This is your safety net. A good CSP tells the browser which scripts are allowed to run, so that even if an attacker injects a script, the browser refuses to execute it. It turns a critical XSS into a non-event. It's the single most valuable defence-in-depth control you can add.
Set HttpOnly on session cookies so that even successful XSS can't read them via JavaScript.
Where Safeey comes in
XSS itself needs code-level review to fully eliminate — but the defences against it are exactly the kind of thing Safeey checks from the outside. Safeey tells you whether you have a Content-Security-Policy in place and how strong it is, whether your cookies are protected with HttpOnly, and it actively probes for reflected-input behaviour on your pages. Those are the guardrails that turn an XSS bug from a catastrophe into a shrug.
Scan your site with Safeey to see whether your XSS safety nets are actually in place.
See what your site exposes
Safeey runs 1,700+ checks and shows you exactly what an attacker would find — with the fixes.
Scan your site