Introduction
F5 Labs attack series articles help you understand common attacks, how they work, and how to guard against them.
What Is Cross-Site Scripting?
Cross-site scripting, commonly referred to as XSS, is one of many types of insertion attacks1 that affect web-based applications and, by extension, their users. It occurs when a vulnerability in an application enables an attacker to insert a malicious script—typically JavaScript—into the vulnerable website’s code. Once the script is inserted, a user’s browser automatically runs the script when he or she visits the affected website. The script can be designed to carry out a variety of malicious acts, leaving the unsuspecting user vulnerable to potential cookie, credential, data, or identity theft; account takeover; malware infection; even full compromise of the user’s system. As such, XSS attacks violate the first two (and potentially all three) of security's foundational CIA triad principles: confidentiality, integrity, and availability.
Given that the malicious script runs client-side in the user’s browser (as opposed to server-side on the web server itself), the website isn’t the ultimate target, its users are. Most users trust that the responses they receive from a web application are legitimately generated by the app and not by a malicious actor. Cross-site scripting exploits that trust. The insidious thing about XSS attacks is that users can become victims simply by unknowingly visiting a compromised website. It’s not obvious or visible to the user that a website has been tampered with, and as long as the website remains compromised, every new visitor will potentially be affected by it.
What Makes a Website Vulnerable to Cross-Site Scripting?
Websites and web-based applications that are vulnerable to XSS are generally those that accept user input in login pages, search boxes, comment fields, sign-up forms, or forms requesting name, address, phone, and credit card numbers. It’s hard to imagine a website today that doesn’t incorporate at least one of these components.
A web application should never automatically trust input from a user without first validating it. That means the application should only accept the “right” kind of input based on what’s expected or asked, for example, exactly five numeric digits for a U.S. zip code, or exactly 16 numeric digits for a credit card number. In addition, it should not accept any kind of input that includes special characters used in code or commands. A website is vulnerable to XSS attack when it doesn’t validate input that is then used in the output (responses) the application sends back to the user. It’s this failure to validate input that enables an attacker to insert a malicious script into the website code. (This is essentially the same type of vulnerability that makes SQL injection attacks possible. The difference with XSS is that the inserted code runs in the user’s browser whereas malicious SQL commands target a website’s database and are sent directly through for processing.)
Types of Cross-Site Scripting Attacks
There are three primary types of cross-site scripting attacks: stored, reflected, and DOM-based. Regardless of type, all three follow the same fundamental attack sequence: (1) an attacker searches for sites that have XSS vulnerabilities, (2) the attacker inserts malicious code into a vulnerable webpage, and (3) when the unsuspecting user interacts with the website, that malicious code is sent to the user’s browser, where it runs automatically.
Persistent (Stored) XSS Attacks
Persistent XSS attacks generally target websites like online communities and Internet forums (also known as message, bulletin, or discussion boards; guest books; social networking platforms) that accept user-generated content. An attacker, for example, posts a seemingly innocuous comment, but the comment contains a malicious script. Later, when a user views that comment (the script itself is not visible), the user’s browser runs the script automatically.
Persistent XSS attacks—more commonly known as “stored” because the malicious code is saved on the web server or in a database—are considered the most dangerous type because any visitor who views the comment becomes an unwitting victim. The attacker doesn’t need to use any tactics to trick or deceive the user.
Reflected XSS Attacks
Reflected XSS attacks (also known as non-persistent) generally occur in websites that mirror information back to the user, for example, the results of a search query, or a greeting such as Welcome [name]! after the user logs in. Unlike stored XSS, reflected XSS attacks involve deception; the attacker must find a way to trick the user into clicking on a malicious link that runs an HTTP command, which then triggers the malicious script. The challenge is in getting the victim to trust that the malicious link is legitimate; often it’s included in an innocent looking email from a source the user trusts.
In one example of a reflected attack, the malicious script could be designed to steal the user’s session cookie, enabling the attacker to impersonate the user and take any action he or she would normally be able to perform while using the application. Imagine if the user was logged into a banking website, the attacker could transfer all of the user’s funds to an account under his or her control. Since the attacker is using a legitimate session cookie (albeit stolen) and appears to be logged in as the real user, there’s nothing suspicious about this transaction that would raise any red flags.
DOM-Based XSS Attacks
Every webpage has a Document Object Model (DOM) that describes the components (things like the title, headings, tables, forms, links, etc.) and logical structure of the HTML page. The browser builds and updates the DOM as it retrieves a webpage and runs scripts. A DOM-based XSS attack is similar to a reflected XSS attack in that the malicious code runs in the browser. The difference is that the code is inserted into the DOM rather than the website. The DOM is just one more place for an attacker to hide a malicious script.
Why “Cross-Site”?People are often confused by the term “cross-site” in reference to XSS. It originally referred to a browser vulnerability that allowed information from one website to be shared with another website if each were open in separate browser tabs at the same time. Most modern browsers have addressed this issue. Today, XSS refers more broadly to attacks that both exploit the inherent trust a user has in the validity of a website and the site’s failure to adequately validate user input. |
How Prevalent are Cross-Site Scripting Attacks?
Although XSS has been known and understood for almost two decades—it’s been on the OWASP Top 10 list of web application vulnerabilities since the first list was published in 2003—it continues to be a common and easily exploitable vulnerability with the potential to cause significant damage. The 2017 OWASP Top 10 report notes that XSS is the second most prevalent web application issue, and it exists in about two-thirds of all applications.2 This estimate is not surprising when you consider that NIST’s National Vulnerability Database (NVD) lists more than 15,000 known XSS vulnerabilities dating back to 2001. Nearly 600 of these were reported in the first three months of 2020 alone,3 yet it’s surprising how many “old” vulnerabilities continue to be exploited today. Although the degree of impact can vary widely, all are nevertheless real vulnerabilities that, depending on context, could have devastating results if exploited.
How to Protect Against Cross-Site Scripting Attacks
Aside from the obvious technical security controls to install updates and patch known web application and browser vulnerabilities, there are additional and equally important ways to guard against XSS attacks.
Input validation. When it comes to websites and web applications, all external input supplied by the user should be considered untrustworthy. Therefore, all input should be validated as described earlier: it should be checked to ensure it meets the requirements of what is allowed and expected, such as five numeric digits for a U.S. zip code or a properly formatted date.
Often, organizations think that denylisting—specifying which characters are not allowed, especially those known to be used in code and commands—provides enough protection. However, allowlisting—specifying exactly what type of input is allowed—is considered far more effective since it’s easy to miss certain unsafe characters using denylisting alone.
Escaping and filtering. Character escaping is the process of first identifying potentially unsafe characters (like <, >, &, and others) that should be interpreted as data rather than executable code and then either marking or replacing them with "safe" representations; filtering removes them entirely. Both actions are often referred to as input sanitization.
Input validation and sanitization should be considered the first line of defense against XSS attacks.
HTTPOnly cookies. A cookie that contains an HTTPOnly flag tells the browser that that particular cookie should only be accessed by the server; it is not accessible by JavaScript's document.cookie API or any others. Use of this flag is designed to prevent an attacker from grabbing a cookie and impersonating a user, which can help to mitigate (primarily) reflected XSS attacks. But be aware: it doesn’t solve the fundamental input validation issue with XSS attacks.
User awareness training. Because XSS attacks ultimately target unsuspecting users, don’t underestimate the importance of providing security awareness training. It’s precisely because of attacks like XSS that are, in effect, “invisible” to the user or originate from seemingly trustworthy sources that users need to be trained to develop safe, defensive online habits. A few tips users should remember:
- Never click on or share links that aren’t verified. Instead, look up the address or domain name manually and evaluate the search results, or copy and paste the link into Notepad to check the full URL without activating it.
- Watch out for suspicious looking domain names that don’t logically reflect the company name, contain misspellings, or use non-standard characters. When in doubt, search for the domain manually.
- Don’t automatically trust a website that displays a padlock, HTTPS as the connection type, or the word “SECURE” in the address bar. Many attackers use encryption now to make their malicious websites appear legitimate. Click the padlock to view the details of the website’s certificate.
In addition to these recommendations, many newer browsers and email clients have built-in security features that can blunt reflected XSS attacks.