Cross-site scripting (XSS) is a web flaw that lets an attacker run their own JavaScript inside another person’s browser, on a site that person trusts. The site fails to clean up some input before showing it, so the browser treats the attacker’s code as part of the page. From there the script can act as the victim, read what they can see and send requests in their name.

This guide explains how cross-site scripting works, the main types, what it costs a business and how to stop it. It is written for IT managers and owners, not developers, so the code is kept to a minimum.
Table of Contents
How does cross-site scripting work?
Every XSS attack needs two things. The site must accept input that an attacker can shape, and it must put that input into a page without treating it as plain text. MDN, the web documentation project, describes the result as an attacker getting a site to execute malicious code as though it were part of that site.
That matters because of the same-origin policy. Browsers normally stop one site’s code from reaching another site’s data. Cross-site scripting sidesteps the rule, because the hostile script arrives from the trusted site itself.
A simple case: a search page prints “You searched for” followed by whatever was typed. If someone types a script tag instead of a word, and the page prints it as is, the browser runs it. The attacker then sends a link containing that search to a victim. One click is enough.
What are the types of XSS attack?
Security teams sort XSS by where the bad input lives and where it runs. PortSwigger and OWASP both describe three main kinds.
- Reflected XSS: the script comes from the current request, such as a crafted link, and bounces straight back in the response. It needs the victim to follow the link, so attackers rely on email and messaging.
- Stored XSS: the script is saved on the server, for example in a comment, a profile field or a support ticket. It runs for everyone who loads that content. MDN calls this form particularly severe.
- DOM-based XSS: the flaw is in the page’s own JavaScript, which writes untrusted data into the page in an unsafe way. The server may never see the payload at all.
OWASP also lists blind XSS, a kind of stored attack where the payload fires in a back-end screen such as an admin panel. The attacker cannot see that screen, but staff with high privileges can trigger it. That is why a harmless-looking contact form can be a route to an administrator account.
What can an attacker do with cross-site scripting?
Injected code can do anything the site’s own code can do. According to MDN, that includes reading and changing loaded pages and local storage. It can also make requests with the user’s credentials.
In practice, OWASP says the worst outcome is theft of session cookies, which lets an attacker hijack a logged-in session. Other results include:
- capturing login details through a fake form placed on the real page;
- redirecting users to another site;
- changing what a page shows, which can harm a firm’s reputation;
- carrying out actions as the victim, such as changing an email address or approving a payment.
The real impact depends on the application. PortSwigger notes that the effect on a brochure site is often minor. A site holding banking, email or health data is a different story. If the victim is an administrator, the attacker may gain full control of the application.
How common is XSS?
Common enough to sit inside one of the best-known lists in web security. In the OWASP Top 10 for 2021, XSS (CWE-79) belongs to the Injection category, which ranked third. OWASP reported that 94% of the applications it analysed were tested for some form of injection, with more than 274,000 occurrences recorded across the category.
Those figures cover all injection flaws, not XSS alone. Treat them as a sign of scale, not an XSS rate. Our own explainer on the OWASP Top 10 covers how the list is built and why it is a starting point rather than a full checklist.
How do you prevent cross-site scripting?
No single control stops it. OWASP’s guidance stresses layers. Use safe framework defaults and correct output encoding. Sanitise anything that must contain HTML. Add a content security policy as a backstop.
Encode output for its context
Output encoding turns characters such as angle brackets and quotes into harmless text before they reach the page. The right encoding depends on where the data lands: an HTML body, an attribute, a URL or a script block each need different handling. OWASP warns against putting untrusted data in script blocks, event handler strings or similar spots at all.
Use framework protections, and watch the escape hatches
Modern templating engines escape output by default. Django does this, for instance. The risk comes when developers bypass the default. OWASP names React’s dangerouslySetInnerHTML and Angular’s bypassSecurityTrust methods as examples to audit. The name is a warning for a reason.
Sanitise HTML you must allow
If users can legitimately submit formatted text, run it through a well-maintained sanitiser such as DOMPurify. Do not edit the result afterwards. A home-made filter with a list of banned tags rarely works, because attackers keep finding ways around it.
Add a content security policy
A strict content security policy (CSP) tells the browser which scripts may run. Even if an attacker injects code, the browser can refuse to execute it. MDN calls this a backup defence, and OWASP calls it defence in depth. Neither treats it as a fix for the underlying bug.
Do not lean on a firewall alone
OWASP is blunt that web application firewalls are unreliable for this job. They miss client-side XSS and do nothing about the root cause. A firewall can reduce noise, but the code still needs fixing.
How do you find cross-site scripting in your own applications?
Automated scanners find a good share of the simple cases. PortSwigger says its scanner finds most XSS quickly. It also notes that some DOM-based flaws are harder to spot and generally need a review of the JavaScript. OWASP makes the same point: scanners find only part of the problem, so code review and manual testing still matter.
That is where a hands-on web application penetration test earns its fee. A tester submits unique input to every entry point, finds where it shows up and checks each context by hand. They also try stored and blind variants that a scanner will not reach. If you want to understand the gap between the two approaches, read our piece on penetration testing versus vulnerability scanning.
If you would like experienced testers to look at an application, you can get in touch with Aardwolf Security to discuss scope. The guidance above stands on its own either way.
Frequently asked questions
Is XSS the same as SQL injection?
No. Both are injection flaws, but they hit different targets. SQL injection attacks the database behind a site. XSS attacks the browsers of the people who use it. We cover the first in what an SQL injection attack is.
Can XSS happen on a small business website?
Yes. Cross-site scripting can affect any site with a search box, a comment form, a contact form or a customer login can have it. Plugins and old themes on content management systems are a common source, so keep them patched.
Does HTTPS protect against XSS?
No. HTTPS protects data in transit. An XSS attack happens inside the page after it has arrived, so the connection can be perfectly encrypted and the page still be compromised.
What is the difference between stored and reflected XSS?
Stored XSS is saved on the server and hits every visitor who loads the content. Reflected XSS lives in a single request, so the victim has to follow a crafted link. Stored attacks usually reach more people with less effort from the attacker.
How often should we test for cross-site scripting?
Test after major changes to an application, and at least once a year as a baseline. Any new feature that takes user input is a fresh place for XSS to appear.
Subscribe to our newsletter
Honest updates, straight to your inbox. Unsubscribe any time.