Americas

  • United States

Asia

Oceania

Josh Fruhlinger
Contributing writer

What is XSS? Cross-site scripting attacks explained

Feature
Mar 08, 202210 mins
CybercrimeInternetMalware

With XSS, attackers enter malicious code into a web form or web app URL to trick the application into doing something it's not supposed to do.

security risk - phishing / malware / social engineering
Credit: Thinkstock

Cross-site scripting (XSS) is a cyberattack in which a hacker enters malicious code into a web form or web application url. This malicious code, written in a scripting language like JavaScript or PHP, can do anything from vandalizing the website you’re trying to load to stealing your passwords or other login credentials.

XSS takes advantage of an important aspect of the modern web, which is that most websites are built on the fly when pages load, sometimes by executing code in the browser itself. That can make such attacks tricky to prevent

How XSS works

Anyone can set up a website that contains malicious code. In a cross-site scripting attack, an attacker sets things up so their code gets on their victim’s computer when the victim accesses someone else’s website. That’s where the “cross” in the name comes from. XSS attacks manage to pull this off without any need to gain privileged access to the web server to plant code on it surreptitiously. Instead, the attackers take advantage of how modern webpages work.

If someone asked you for a basic, entry-level explanation of the web, you would probably tell them something like this: a person who wants to create a webpage writes an HTML document, which they upload to a web server; when a user wants to access that page, they point their browser to the server’s address, and the browser downloads the HTML code and interprets it to build a version of the web page for the user.

That description isn’t wrong, exactly, but there are aspects that are outdated (and have been for a decade or more). First of all, many if not all web pages are now dynamic—that is, they don’t show the same static HTML code to every visitor, but rather are built on the fly from information contained in the server’s database when a browser requests access. What page the browser gets back from the server often depends on information it sends with its request—information that sometimes takes the form of parameters in the URL used to access the site. And websites don’t just consist of HTML and cascading style sheets (CSS)  that describe how text and graphics should be rendered; they also include executable code written in scripting languages, usually JavaScript. Intermingling data, presentation, and executable code in this way is a sort of “original sin” of web security.

In an XSS attack, a hacker takes advantage of this interaction between a user and a website to get malicious code to execute on the user’s machine. But how? Consider the following URL: 

https://www.google.com/search?q=CSO+online

Put that into your browser address bar and you’ll see the Google search results for “CSO Online.” In fact, the page you’ll see looks exactly as if you had typed “CSO Online” into your browser search bar, or into the Google.com front page. Among other things, you’ll notice that the phrase appears in several places on the page, including the search bar at the top:

browser bar CSO

Now, what if instead of the innocent and wholesome phrase “CSO online,” that URL had contained malicious JavaScript code, like this?

https://www.google.com/search?doEvil()

doEvil() here is standing in for some truly nasty functionality. You might worry that Google, instead of displaying “CSO Online” on the page it returns from this URL, will instead incorporate that evil JavaScript into the webpage it dynamically renders, and that script will then execute in the browser, wreaking havoc. Your worry would be unfounded, because Google has quite a few talented IT security pros on staff and has implemented mitigation measures of the sort that we’ll discuss in a moment. But not every site is so careful, and this gives you a sense of how these attacks can work.

XSS attacks

XSS attacks are broken down into several categories: reflected attacks, DOM-based attacks, and stored attacks. Here’s how they differ:

Reflected attacks: The attack described above would be called a reflected or non-persistent attack, because the evil JavaScript was sent from the victim’s web browser to Google and then reflected back in executable form, and is never stored persistently on Google’s servers. These attacks are often part of a phishing scam, where the evil link is disguised as something more palatable and sent to the victim via email or text message.

DOM-based attacks: This is a variation on the reflected attack and is named after the Document Object Model, a standardized API that defines the way browsers build a web page out of underlying HTML or JavaScript code. Most DOM-based attacks are similar to the reflected attack we just described, except that the malicious code is never sent to the server: instead, it’s passed as a parameter to some JavaScript function that’s executing in the browser itself, which means that server-side defenses can’t protect the user. PortSwigger has a more in-depth look at how DOM-based attacks work if you’re interested in the details.

Stored attacks: The final main type of XSS attack is a stored or persistent attack, and it’s somewhat more straightforward, though also quite dangerous. In a stored attack, the attacker manages to use a site’s interactive features to store some malicious code on the web server. In a common example, an attacker would leave a comment on a blog post that includes malicious JavaScript. The next time anyone loads that page, the code would execute.

XSS vulnerability

What makes a website vulnerable to an XSS attack? Hopefully our discussion so far has made given you a hint. If your web application naively takes in user input, doesn’t check it for potentially malicious executable code, and uses that input to render a web page or perform other operations, it’s vulnerable to this sort of attack.

And the stakes are high. A crucial aspect of browser security is what’s known as the same-origin policy, which dictates that a script executing on one page can only access data on a second page if the two pages share an origin (defined as the combination of a URI scheme, host name, and port number). But if malicious JavaScript can execute in the browser while the victim is accessing a website, the browser will let that JavaScript access data from other pages that share an origin with the vulnerable page. That JavaScript can access cookies and other restricted session information, which adds up to a good recipe for breaking into victims’ online accounts.

XSS payloads

In malware lingo, a payload is executable code that performs the actions that the attacker wants performed. In an XSS attack, the payload is the script code that the attacker manages to trick the victim’s browser into executing.

The payloadbox repository on GitHub has a huge list of sample XSS payloads. If you know JavaScript, reviewing these can give you a sense of how XSS attackers do their dirty work. However, an XSS attack goes way beyond cutting and pasting a payload into a URL: the attacker would need to understand the specific functionality and vulnerabilities of the web application they’re trying to compromise in order to plan their assault. If you want to go deeper and see some XSS attack examples, check out OWASP’s XSS page, which goes into depth with script code that illustrates an XSS attack.  

XSS protection and prevention

If you’re building or operating a web application or interactive website, there are three main techniques you should be integrating into the design to mitigate against potential cross-site scripting attacks.

  • Input sanitization. The obvious answer to preventing malicious script code from being passed in as input and reflected to the user is to simply not accept script code as input in the first place. You should definitely be filtering input with this in mind, but this is easier said than done; cleverly crafted snippets of executable code can be slipped through filters. One way to deal with this is to take an whitelist rather than a blacklist approach—for instance, rather than trying to write a filter that blocks all malicious code from being entered in a web form, write your application so that it only accepts data in specified formats (phone numbers, email addresses, etc.) if that’s what you’re expecting.
  • Output escaping. This tackles the problem from the other direction. If your web application sends data entered by the user back to a web page, that data should be filtered to make sure it doesn’t become executable code on that web page. If the user enters HTML tags code as input, your application should use escape characters to ensure that those tags appear as text on the resulting page, rather than being integrated into the page HTML itself.
  • The Content Security Policy (CSP) standard. CSP takes the whitelist approach past just input text and into the realm of script execution: your web application should only ever execute the specific code that you’ve told it is safe. Google has some great resources on implementing CSP.

XSS test

Testing for XSS vulnerabilities is an important aspect of keeping your web application secure. OWASP has resources that get in-depth on how you can test your applications for vulnerability to DOM-based or reflected cross-site scripting attacks. If you’re looking for an XSS cheat sheet, OWASP has you covered there as well with a document full of code for XSS attacks that can be used to bypass certain XSS defensive filters; these will prove invaluable for any penetration testing you might be doing against your own systems.

XSS vs CSRF

You might hear CSRF used in the same context as XSS. This stands for cross-site request forgery, which is an attack that, like XSS, targets a user’s browser. The main difference is that CSRF exploits a user’s authenticated session (maybe they are logged into their bank account), while XSS doesn’t need an authenticated session to be effective.

Let’s say you were logged into Twitter and online banking at the same time, and you clicked on a Twitter link that looked like this:

http://www.yourbank.com/sendmoney,do?from=you&to=attacker&amount=5000

Depending on how your bank manages session tokens, and what browser you’re using, you might be five grand poorer. XSS is a more dangerous attack vector, but it’s important to defend against both XSS and CSRF. OWASP has a cheat sheet for CSRF defensive security measures as well.

Recent and famous XSS attacks

One of the earliest and most famous cross-site scripting attacks came in 2005, when enterprising MySpace user Samy Kamkar figured out that he could insert JavaScript code into his profile that would automatically make friends with anyone who visited the page—and also copy the code into his new friends’ profiles, so anyone who visited those pages also friended him. (The script made sure that each of Kamkar’s new friends had him as one of their “Top 8,” something we’re afraid you really had to have been there at the time to understand, but trust us when we say it was important.) Within 24 hours, he had made more than a million friends and forced MySpace to briefly take its entire site offline.

The so-called Samy worm turned out to be mostly harmless. But others were much more troubling:

And cross-site scripting remains a major threat today. Since 2021, XSS vulnerabilities have been found in the Zimbra email platform, WordPress, and the Nagios open source IT management platform. And remember, hackers generally don’t use attack techniques in isolation: cross-site scripting is one component in a complex and recently discovered form of attack using wildcard TLS certificates called ALPACA, for instance. You need to make sure your XSS vulnerabilities are closed to stave off big risks.