Cross-site scripting (XSS) is a common security flaw found in websites and web applications. It enables attackers to inject harmful code, typically in the form of client-side scripts, into web pages that are viewed by users. Once the code is executed by the user’s browser, it can manipulate the site’s behavior, steal private data, or carry out actions as though they were the user.
XSS vulnerabilities arise primarily from insufficient validation or escaping of user input and dynamic content. This oversight allows the injection of malicious JavaScript, which can then be executed within the user’s browser. Essentially, if an application fails to properly validate or encode user-provided input before incorporating it into the output, it opens the door to XSS attacks.
Exploiting XSS weaknesses can have serious consequences, such as session hijacking, website defacement, redirecting users to harmful websites, or even logging keystrokes. The risk is especially pronounced in applications that handle user input. To mitigate these dangers, it is essential for developers to implement rigorous input validation and output encoding techniques.
By addressing XSS vulnerabilities, developers can help safeguard their applications and enhance the overall security and user experience. Fortunately, there are a number of categories of security testing tools available that will spot weaknesses, including XSS vulnerabilities. We will cover these later in this guide.
Types of XSS
XSS comes in many different forms, which can be categorized into the following groups:
- Reflected (non-persistent) XSS: Just as the name implies, reflected XSS occurs when the injected malicious script results show up or are immediately reflected by the user without adequately sanitizing the content.
- Stored (persistent) XSS: This is a more devastating variant of a cross-site scripting flaw. It occurs when the data provided by the attacker is saved by the server and then permanently displayed on “normal” pages returned to other users in the course of regular browsing, without proper HTML escaping.
- DOM-based XSS: DOM-based XSS occurs when the injected malicious code does not get to the webserver. Instead, it is reflected by client-side JavaScript code on the client-side.
XSS is one of the most common vulnerabilities discovered on web applications. If left unpatched, XSS can expose your application to various security risks that negatively impact the organization and end-users. An XSS vulnerability that allows an attacker to modify customer product quantity balance or bank account balance could affect the reputation of a company or weaken consumer confidence. This is something you can’t afford to ignore. Therefore, it is essential to identify and remove this vulnerability from your web application before it exposes it to serious security problems. This article will explain how to find XSS in web applications, including what you can do to prevent it.
Manual Code Reviews and Testing
Code review aims to identify security flaws in applications, along with the exact root causes. It involves auditing the source code of an application to ensure that it is self-defending in its given environment. According to OWASP, “If code has not been reviewed for security holes, the likelihood that the application has problems is virtually 100%”. Tools can be used to perform this task. Still, unfortunately, they do not understand the context (human verification is always required), and this is where manual security code review comes into play.
If manual code review is done correctly, a penetration test afterward using automated tools should discover little or no vulnerabilities. Interestingly, OWASP provides a detailed guide on manually reviewing code for XSS vulnerabilities, including a complete manual testing guide for reflected XSS, stored XSS, and DOM-based XSS vulnerabilities for your reference. The following manual processes can be used to identify common XSS vulnerabilities:
Identify code that outputs user input: Codes that output user input without proper sanitization risk XSS vulnerability. Press Ctrl + U to view the page output source from the browser to see if your code is placed inside an attribute. If it is, inject the following code and test to view the output: “onmouseover= alert(‘hello’);”
You can test to view the output using this script: <script>alert(document.cookie);</script>;
However, if the code you are reviewing filters for <and> characters, then test using the following script instead: &{alert(‘hello’);}
Check that output is encoded: Check that HtmlEncode is used to encode HTML output that includes any type of input. Also, check that UrlEncode is used to encode URL strings. Input data can come from query strings, form fields, cookies, HTTP headers, and input read from a database, particularly if other applications share the database. If these encodings are missing, your application is at risk of XSS vulnerability. In addition, by encoding the data, you prevent the browser from treating the HTML as an executable script.
Identify code that handles URLs: Code that governs URLs can risk XSS and other vulnerabilities. Review your code and systems as follows:
- Check that your webserver is updated. If your webserver is not up-to-date with the latest security patches, it could be vulnerable to directory traversal attacks:
- If your code filters for “/”, an attacker can easily bypass the filter by using URL encoding (percent-encoding) for the same character. For example, the percent-encoding for “/” is “%c0f%af”, which could be used to bypass the filter as shown in the following URL: http://www.abc.com/..%c0f%af../winnt.
- If your code processes query string input, check that it constrains the input data and performs bounds checks. Also, check that the code is not vulnerable if an attacker injects a massive amount of data through a query string parameter like the URL shown here: http://www.abc.com/test.aspx?var=InjectHugeAmountOfDataHere.
Unit test your codes: Unit testing is a software testing method by which individual units of source code are tested to determine whether they are fit for use. Unit testing aims to isolate each part of the program and show that the individual components are correct. Use unit testing to make sure that a particular bit of data is correctly escaped. Unit testing helps to identify XSS and other flaws early in the development cycle. If possible, unit test every place where user-supplied data is displayed. After you find and fix an XSS bug in your code, consider adding a regression test for it.
Perform these basic tests on your application: Create a test user profile and use that profile to interact with your application. Insert strings that contain HTML and JavaScript metacharacters such as >’>”><img src=x onerror=alert(0)> into all application inputs, such as forms, URL parameters, hidden fields, or cookie values.
If your application doesn’t correctly escape this string, you will see an alert and know that something isn’t right. Wherever your application handles user-supplied URLs, enter the following javascript code: alert(0) or data:text/html,<script>alert(0)</script>. All of these can help identify stored XSS bugs.
Automated Application Testing
Standard security testing techniques and tools can be utilized to test and detect XSS vulnerabilities in web applications. Some of the popular testing techniques that can be used include:
- Static Application Security Testing (SAST): SAST is used to secure applications by reviewing the source code to identify vulnerabilities or evidence of known insecure practices when it’s not running. A significant benefit of static code analysis is you don’t need to wait until the application is deployed in a staging environment with test data. Instead, you can just test the code. This gives the inspection for vulnerabilities 100% code coverage and makes finding vulnerabilities faster and cheaper. SAST tools employ a white-box testing strategy that scans the source code of applications and their components to identify potential security flaws.
- Dynamic Application Security Testing (DAST): DAST tools communicate with applications to identify potential security vulnerabilities through the front-end. DAST tools do not have access to source codes; instead, they perform attacks using the black-box strategy to detect vulnerabilities. With dynamic analysis, security checks are performed while running or executing the code or application under review. A technique known as fuzzing is used in dynamic tests to submit random, malformed data as inputs to the application to determine if it will uncover XSS flaws.
- Interactive Application Security Testing (IAST): IAST combines the best of SAST and DAST. It analyzes code for XSS and other security vulnerabilities while the app is run by any activity interacting with the application functionality.
With the techniques described above, you can quickly test and detect XSS vulnerabilities in your web applications. Web vulnerability scanners such as Veracode, Checkmarx, Invicti, Acunetix, and others are powerful tools that can crawl your entire website or application and automatically checks for XSS and other security flaws. While they are often not optimized for your particular application, they allow you to quickly and easily find the more obvious vulnerabilities. In addition, they implement most of the web application testing techniques discussed above and will enable you to apply those techniques to scan your web application for vulnerabilities. It will then generate a report on the vulnerabilities found and attempt to fix them automatically
What You Can Do to Prevent XSS
This section provides some recommendations on how you can minimize XSS vulnerabilities. They are by no means exhaustive; however, they should be a good starting point to secure applications. Nothing is foolproof. Technology is changing very rapidly, and attacks are getting more sophisticated. What works today may not fully work tomorrow. But by understanding the basics, you can be prepared to prevent future attack techniques as they evolve. Here are some of the things you can do to prevent XSS.
Use secure coding practices: As with most injection vulnerabilities, the key to securing your web application and preventing XSS attacks is to adhere to ensure coding practices. A user input scenario to be mindful of in your applications is when your application accepts input from users and uses that input to create output for other users. This is where the golden rules of user input come to play. The first golden rule of user input states that all information is suspicious until proven otherwise. The second golden rule of user input says that data must be filtered as it crosses the boundary between untrusted and trusted domains. The moment you neglect these rules, you open the door for XSS and other injection attacks. OWASP provides a technology-agnostic document that defines a set of general software security coding practices in a checklist format that can be integrated into your software development lifecycle. Implementation of these practices will mitigate most common application vulnerabilities, including XSS.
Filter user inputs: One of the simplest ways to prevent XSS vulnerability is passing all external data (user input) through a filter. Such a filter would remove dangerous tags and attributes such as <script>, <applet>, <html>, <frame>, <img>, <embed>, and other HTML markups and JavaScript commands. You may choose to implement your XSS filter mechanisms or use existing tried and tested libraries. Although some experienced attackers often manage to circumvent simple filters by using hex encoding, unicode character variations, line breaks, and null characters in strings. This is why user input filtering is not considered sufficient enough to defend against advanced XSS attacks. Check out the OWASP filter evasion cheat sheet, which lists a series of XSS attacks that can be used to bypass certain XSS defensive filters to learn more on how to guard against them.
Use escaping techniques: A common technique for preventing XSS vulnerabilities is “escaping”. When you use escaping techniques, you effectively tell the web browser that the data you are sending should be treated as data and not be interpreted as a control character or code. Thus, if an attacker manages to inject an XSS script on your page, the malicious code will not be executed if it is properly escaped. However, to guarantee the security of your web applications, you must master the use of escaping techniques for HTML, JavaScript, CSS, and sometimes XML data. This is because these languages require different forms of escaping for each context.
In HTML, you can escape dangerous characters by using HTML entities such as &# sequence followed by its character code. For example, ‘<’ and ‘>’ are the HTML encoding for the ‘<‘ and ‘>’ characters respectively. If you include: <script>alert(‘testing’)</script> in the HTML of a page, the script will execute. But if you include the character codes in the HTML of a page as shown here: <script>alert(‘testing’)</script>
It will print out the text “<script>alert(‘testing’)</script>”, but it will not actually execute the script. By escaping the <script> tags, we prevented the script from executing. Use HTML escaping when untrusted data is inserted between HTML opening and closing tags.
Similarly, use JavaScript or CSS escaping when untrusted data is inserted inside one of your scripts or CSS styles, as the case may be. This includes event handlers such as on mouseover and upload and certain HTML attributes such as style. For example:
<script>alert(‘If this data is untrusted, it must be JavaScript-escaped.’)</script>
<body onload=”If this data is untrusted, it must be JavaScript-escaped.”>
You can do all the escaping by writing the code yourself. However, writing your code for escaping user input and adequately and consistently applying it is extremely difficult. Therefore, we recommend using existing libraries such as OWASP ESAPI, Microsoft AntiXSS (best suited for Microsoft environment), or web development frameworks or templates that provide context-aware auto-escaping. If you use templates to generate HTML within JavaScript, Closure Templates and Angular provide built-in escaping capabilities.
Other things you can do to prevent XSS vulnerability or minimize the chances of XSS attacks include:
- Learning the best practices for your server-side and client-side technologies, such as using HTML sanitizer or configuring your browser to use a proxy that scans and intercepts traffic such as Burp Proxy and ratproxy to help identify problems.
- Using a web template system or a web development framework with context-aware auto-escaping.
- Manually escaping user input (if template system with context-aware auto-escaping is not feasible)
- Understanding common browser behaviors that lead to XSS.
Conclusion
There is no silver bullet for detecting XSS in web applications. Instead, finding XSS vulnerabilities requires a combination of human effort (manual code reviews) and technology support (automated tools such as vulnerability scanners). A developer and an application security reviewer manually checking codes with a text editor at one end of the scale. An expert security team with advanced static analysis (SAST) tools is at the other end of the scale.
Tools are good at assessing large amounts of code and pointing out possible issues. Still, a person needs to verify every result to determine if it is exploitable and possibly evaluate the risk to the organization. Human reviewers must fill in for the significant blind spots, which automated tools simply cannot check. No testing methodology is foolproof; so, performing a combination of manual code reviews and automated testing will decrease the odds of an XSS vulnerability in your application.
Cross-site scripting FAQs
Where can you typically find XSS vulnerabilities?
Cross-site scripting attacks are implemented through user input fields in websites. So, it is important to block automatic posting into a website. Bulletin boards and comments sections on Web pages are the most susceptible Web features for XSS vulnerabilities.
How is XSS detected?
The easiest way to detect XSS vulnerabilities is to use a vulnerability scanner. You can implement manual code checks in a Web page. If you are not a coding expert, you might find this task difficult. The Open Web Application Security Project (OWASP) recommends the following:
- Ensure that input isn’t carried in the same HTTP responses as HTML or JavaScript.
- Encode all data for transmission unless it is reference values that have come from your own database and you are sure that it could not have been tampered with.
- Be careful about how data is introduced into the document object model (DOM). wrap data in one of the following APIs:
- Node.textContent
- document.createTextNode
- Element.setAttribute (second parameter only)
Can WAF detect XSS?
You can expect that a Web application firewall (WAF) will spot and block an XSS attack. However, if one occurs, that means your website has coding weaknesses that make XSS attacks possible and they will reoccur until you rewrite those processes to close down the vulnerability. Use a vulnerability scanner to spot security weaknesses that make cross-site scripting attacks possible, then you won’t have to worry about any attempts that may occur because they should always fail.