A developer at a mid-sized e-commerce company noticed something strange in their support tickets. Customers were reporting that after logging in, they were getting redirected to an unfamiliar page asking them to “re-enter” their card details. The site’s code hadn’t changed. The database hadn’t been touched. But someone had quietly injected a script into the product review section, and it had been running on every page load for three days before anyone caught it.
That’s cross site scripting. And it’s one of the most common ways attackers get into web applications without ever touching the server directly.
What Cross Site Scripting Actually Is
Cross site scripting, commonly written as XSS, is a type of attack where an attacker injects malicious JavaScript (or other client-side code) into a web page that other users then load in their browsers. The key point is that the attacker isn’t attacking the server. They’re using the website as a delivery mechanism to attack the site’s visitors.
The browser sees the injected script and treats it as legitimate code from the website. It has no way to tell the difference. So it runs it. And that script can do a lot of damage — steal session cookies, capture keystrokes, redirect users, or silently send data to a server the attacker controls.
XSS has consistently appeared in the OWASP Top 10 list of web application security risks for years. That’s not because it’s new or exotic. It’s because it keeps working, especially on sites that don’t sanitize user input properly.
The Three Types of XSS Attacks
Understanding how XSS is delivered helps you see where the actual vulnerability lives.
Stored XSS
This is the most dangerous type. The attacker submits malicious code through an input field, a comment box, a username field, a product review form and the server saves it to a database. Every time any user loads that page, the script runs in their browser. The example from the opening story was stored XSS.
Reflected XSS
Here, the malicious script is embedded in a URL. The attacker tricks a victim into clicking a crafted link. The server processes the request, reflects the input back in the response (like in an error message or search result), and the browser executes it. This one is temporary it doesn’t persist on the server. But it’s very common in phishing attacks.
DOM-Based XSS
This type doesn’t involve the server at all. The vulnerability lives in the browser-side JavaScript of the page itself. The page’s own script reads something from the URL or another source and writes it to the DOM without sanitizing it first. The attack happens entirely in the browser. It’s harder to detect because nothing malicious ever gets sent to or returned from the server.
What an Attacker Can Do With XSS
Session hijacking is the most common goal. If a site stores session tokens in cookies without the HttpOnly flag, an attacker’s script can read that cookie and send it to their own server. With the session token in hand, they can log in as the victim without ever knowing the password.
Beyond that, XSS can be used to rewrite parts of the page, swapping out a login form with a fake one, for example. It can be used to log every keystroke the user makes on that page. It can silently make requests on the user’s behalf, which is where XSS and CSRF attacks start to overlap.
On a high-traffic site, stored XSS can affect thousands of users before anyone notices.
Where XSS Vulnerabilities Actually Come From
The root cause is almost always the same thing, user input being inserted into a page without sanitization or encoding. Anywhere your application takes input from a user and later displays it back in the HTML, you have a potential XSS entry point.
Search boxes that show “You searched for: [term]” are a classic example. Comment sections. Profile bios. Error messages that echo back what the user typed. Even URL parameters that get rendered into the page can be vulnerable if the code doesn’t handle them carefully.
Content management systems and plugins are a big attack surface here. If you run a WordPress site, third-party plugins that handle forms, search, or user input can introduce XSS vulnerabilities that the site owner never wrote themselves. Running a scan with the WordPress Security Scanner on your site can surface outdated plugins or known issues before they become a real problem.
How to Actually Prevent XSS
Output encoding is the most important defense. Before inserting any user-supplied data into your HTML, encode it so the browser treats it as text, not code. A script tag gets converted to its HTML entity equivalent, and the browser renders it as visible text instead of executing it as code.
Input validation adds another layer. Not everything needs to accept HTML. A phone number field should only accept digits. A name field doesn’t need angle brackets. Reject or strip what doesn’t belong.
Content Security Policy (CSP) is a HTTP response header that tells the browser which scripts are allowed to run on a page. Even if an attacker manages to inject a script, a properly configured CSP can block it from executing. You can check what HTTP headers your site is currently sending with the HTTP Header Checker to see if CSP is already in place or missing entirely.
Setting the HttpOnly flag on session cookies limits the damage if XSS does occur. A script can’t read a cookie with that flag set. It doesn’t prevent the attack, but it removes one of the main things an attacker is trying to steal.
Modern JavaScript frameworks like React and Angular escape output by default, which helps a lot. But they’re not bulletproof. Developers who bypass the escaping intentionally can reintroduce the vulnerability.
What Most Developers Get Wrong About XSS
A lot of developers think input sanitization at the form level is enough. It’s not. Sanitizing on the way in is useful, but the real defense is encoding on the way out — at the point where the data gets inserted into the page. Those are two separate things, and skipping output encoding while relying on input sanitization alone leaves you exposed.
Another common assumption is that XSS only matters for sites that handle sensitive data. That’s not accurate. Any site with user accounts, sessions, or forms is a target. Attackers can use a low-stakes site to test techniques, or they can use it as a launchpad to attack users who share credentials across platforms.
Some developers also assume that using a framework means they’re safe. Frameworks reduce the risk significantly, but they don’t eliminate it. Custom code that handles raw HTML, third-party libraries, and legacy code paths can all bypass the protections a framework provides.
Finally, XSS isn’t just a developer problem. Site owners who install plugins without vetting them, or who run outdated CMS versions, carry real risk too. The vulnerability doesn’t care who introduced the code.
The Bigger Picture
XSS attacks work because web pages are built to run code. That’s not a flaw in the design of the web, it’s how the web works. The vulnerability comes from mixing untrusted data with executable contexts without telling the browser what’s data and what’s code.
The good news is that the fixes are well understood. Output encoding, CSP headers, HttpOnly cookies, and regular security scans cover most of the risk. None of these require exotic tooling or a security team. They require consistency.
For more on how XSS is classified and what mitigation techniques look like in practice, the OWASP XSS Prevention Cheat Sheet is the most thorough and up-to-date reference available.



