Cross-Site Scripting (XSS) - Risks and Countermeasures
Cross-site scripting has been at the top of the OWASP Top 10 for nearly a decade. In this article, we'll explore everything you need to know about XSS, the associated risks, and countermeasures you can take.
This article is about:
- How Does Cross-Site Scripting Work
- How XSS Occurs
- Types of XSS Attacks
- Why Websites Are Particularly Susceptible to XSS Attacks
- The Aftermath of XSS Attacks
- How to Protect Yourself Against XSS Attacks
- How to Prevent Cross-Site Scripting
How Does Cross-Site Scripting Work
Cross-Site Scripting (XSS) is an attack during which a malicious actor tries to insert snippets of code into dynamically generated websites. When those websites are delivered to the user's browser, the injected script can modify the look and behavior of it and even take actions in the background without the user knowing it. When thinking about online banking or other platforms one may routinely log in to, it becomes clear that this can pose a significant threat.
Once the script is injected into the page, web browsers have no way to tell it apart from the other legitimate scripts that are served along with it. That's why it's extremely important that the web applications themselves implement countermeasures and are regularly dynamically tested under real-world conditions to check if they hold tight.
Jon<script>alert(‘XSS!’);</script>Doe
Generally, any website or application is vulnerable to Cross-Site Scripting if it uses unsanitized user input in the output it generates. Sometimes, the malicious script is also stored in the web application or in a database. However, the final goal is always to deliver the script to a client's browser, where it is executed to perform actions. XSS attacks occur when user input is accepted as part of a request and then used in the output without proper encoding for validation and sanitization.
The most simple example of this can be shown with a website offering its users to create an account. Of course, we'll need to pick a username. A crafty attacker might start by trying things like “Jon<script>alert(‘XSS!’);</script>Doe”. Since usernames are often displayed in many places and shown to other users of the web application, this is an ideal target. If the server doesn't take care of sanitizing the "<script>" tags, the attacker can now execute code in other people's browsers.
How XSS Occurs
To perform XSS, attackers first need to find a way to inject the malicious code (payload) into a website the victim visits. The execution can be triggered in other ways when a webpage loads or a user hovers over specific elements, such as hyperlinks.
If the website or app lacks proper data sanitization, the malicious script executes code on the victim's system. Since the script comes from a seemingly trusted website, the victim's browser runs it without questioning its legitimacy.
Since the malicious script acts on behalf of the trusted website, it will be able to access potentially sensitive information stored on the client's browser, including session tokens and cookies.
Types of XSS Attacks
XSS comes in three primary flavors: stored, reflected, and DOM-based XSS attacks.
- Stored XSS is also known as a persistent or Type-1 XSS. Here, the attacker injects a payload and saves it in the website or web application database. The malicious script is executed alongside the site's legitimate code during a request.
There is another subcategory of stored XSS attacks known as Blind XSS. With a blind XSS attack, the attacker targets a part of the system that he himself cannot access and thus cannot check immediately if the injection was successful (hence the name "blind"). A target for this could be a contact form whose content is later displayed to an administrator and not to the clients.
- Reflected XSS is also known as non-persistent or Type-2 XSS. In reflective XSS attacks, the hackers do not store the payload in the application or website's infrastructure. Instead, it is reflected off the server as a response to a specifically crafted link.
Reflected XSS is much more widespread in web applications and is considered to be less harmful as it is a one-time attack where the payload is only valid on one request.
- A DOM-based XSS attack occurs when users click on a link created by an attacker. Attackers then embed the payload into the malicious URL link. From there, it is passed to the browser's Document Object Model (DOM), where it is executed. This happens because the browser interprets the request as coming from a trusted website or application.
Why Websites Are Susceptible to XSS-Bugs
XSS attacks are possible in pretty much any client-side programming language, including ActiveX, VBScript, and Flash, but they are most common in JavaScript. Some people may find this difficult to agree with, given the controlled environment JavaScript files are executed in and their limited access to the operating system.
While JavaScript is a powerful language for building dynamic web applications, it's equally powerful for attackers when they are able to inject their own code, as it allows attackers to access large parts of the entire web backend.
Moreover, JavaScript-based XSS attacks have far-reaching consequences. For instance, website defacement since the injected malicious script can modify the content displayed by the browser. In more severe cases, criminals can change a product's documentation or alter a press release to ruin the reputation of a target company. Industry specialists recommend thorough testing of Java applications (for vulnerability and flaws) using negative testing tools that can handle their complexity.
The Aftermath of XSS Attacks
The damage caused by a successful XSS attack varies from a petty nuisance to significant security risks, depending on the sensitivity of the data handled by the vulnerable site and the nature of any security mitigation implemented by the site's owner.
Generally, potential consequences of Cross-Site Scripting attacks (for end users, website owners, and developers) include:
- Redirecting users to malicious websites
- Stealing usernames, and passwords, thus compromising a victim's account
- Modifying or defacing the affected sites
- Running web browser exploits (e.g., crashing the web browser)
- Financial loss/fraud
- Credit card information theft
- Reputation damage
- Customer churn
- Incident response and recovery costs
- Compliance fines
- Legal costs, including lawyer fees arising from potential lawsuits for contract violation and liability, as well as customer reimbursement
9 Tips to Protect Yourself Against XSS Attacks
Here are some tips that will help you successfully mitigate Cross-Site Scripting:
1. Make Sure Dependencies Are Up-To-Date
As a general rule of thumb, adopt effective patch management or vulnerability management programs and ensure that you install high-priority and critical patches as soon as they are released. In addition, look out for blocklisted code libraries, minimize third-party code dependency, and always ensure you have secure access to code libraries.
2. Run Regular Security Audits
Even if you follow all the best coding practices, slight mistakes during core development or updates will inadvertently introduce an XSS vulnerability. Thankfully, when you scan your websites or applications regularly for vulnerabilities, you can quickly identify a loophole and fix it before it becomes a big problem.
3. Set Up a Firewall
Another way to avoid XSS attacks (as well as various other attacks) is by using a web application firewall that continuously scans and intercepts any activity that seems like an XSS attack, giving your site an extra layer of protection. However, WAFs do not offer 100% protection and should be your last resort—being secure by design is considered the best practice.
4. Validate User Input
Another great way to prevent XSS attacks is to validate every input field. Consider limiting user input to a specified list whenever you can. For instance, you can require all content to be alphanumeric and block HTML or tags used in XSS.
5. Encode Output Data
At the point where user-controllable data is output in HTTP responses, encode the output to keep it from being interpreted as active content. Depending on the output context, this might require applying combinations of URL, HTML, JavaScript, and CSS encoding.6. Create a Content Security Policy (CSP)
Creating and implementing a content security policy (CSP) is an effective way of mitigating Cross-Site Scripting and other vulnerabilities. It prevents XSS by white-listing URLs from which browsers can load and execute scripts. The server prevents the client's browser from executing any script from an untrusted URL. The CSP acts as an allow list where only domains listed can run while everything else is blocked.
7. Use Safe Cookies (e.g., HTTP only)
Malicious JavaScript can be used to steal cookies containing users' session IDs. And since there is hardly the need to manipulate or read cookies in client-side JavaScript, consider marking cookies as HTTP-only. This means that cookies can only be received, stored, and sent by the user's browser but cannot be read or modified by JavaScript (including malicious JavaScript). You may also want to adopt other stringent cookie rules, like tying them to a specific IP address, to keep attackers from using them during XSS attacks.8. Test Your Software Continuously (Including Negative Testing)
Testing can help you identify and fix bugs and security issues early. To prevent XSS and other injection vulnerabilities, negative testing approaches like
feedback-based fuzzing are highly effective. Compared to a unit testing approach, in which testers approach the system under test with a hunch of where the issues may lie, fuzzers test systems using unexpected and unusual inputs while constantly refining test inputs based on code coverage feedback from the system under test. For larger projects, it is considered best practice to integrate feedback-based fuzz testing
into the C/CD pipeline, to run tests continuously (e.g., at each pull request).
How to Prevent Cross-Site Scripting With Feedback-Based Fuzzing
Fuzzing is an automated software testing technique that injects invalid, malformed, or unexpected inputs into a system to find software defects and exploitable software vulnerabilities, including XSS.
Feedback-Based Fuzzing
Feedback-based fuzzers instrument the tested software to collect data about its interaction with test inputs/requests. This data is then sent back to the fuzzing engine, where it is used to craft further, more refined test inputs that can traverse larger parts of the source code. This allows feedback-based fuzzers to increase code coverage while uncovering vulnerabilities that are deeply hidden within the system under test.
Since feedback-based fuzzers test applications during execution, they always spit out the crashing input when they find a bug, making it easy to fix it.
Example of an XSS-bug found with Fuzzing WebGoat
How to Fuzz Your Own Code
If you are looking for an enterprise testing solution that can help to prevent issues such as XSS on a large scale, check out our Code Intelligence testing platform for CI/CD-integrated fuzz testing. It's compatible with C/C++, Java, Go, JavaScript, and many more languages. Integrating feedback-based fuzz testing into your CI/CD will speed up your development process while giving you the means to find and fix vulnerabilities long before they ever make it into a finished product.
If you want to get started with fuzz testing right away, check out CI Fuzz, an open-source tool that enables automated fuzz testing from the command line.