Cross-site scripting (often abbreviated as XSS) is by far the most prevalent security vulnerability on the Internet today. Hardly a day goes by without a new report of a successful XSS attack against a prominent website. It is my professional opinion that no software testing is complete without a full battery of XSS tests being performed. How can we call ourselves professionals and let this common, simple flaw slip through time and time again?
What is cross-site scripting?
TechTarget’s definition of cross-site scripting captures the brutal truth about the attack:
Cross-site scripting (XSS) is a security exploit in which the attacker inserts malicious coding into a link that appears to be from a trustworthy source. When someone clicks on the link, the embedded programming is submitted as part of the client's Web request and can execute on the user's computer, typically allowing the attacker to steal information.
I consider XSS attacks a temporary identify theft -- a well-crafted XSS attack executes code in the user’s context. The most serious consequences include disclosure of user session information (such as the theft of an otherwise-secure session cookie), clickjacking (the downloading of malware), and spoofing (the display of malicious Web content in an apparently benevolent context). XSS’s insidious nature is rooted in the fact that the user never knows it is happening. We have the responsibility to educate ourselves, our teams and our clients on XSS testing, and root out the risks before our Web applications enter production.
How to test for cross-site scripting vulnerabilities
So how do we test for cross-site scripting vulnerabilities? First of all, there are tools to aid in this. No tool can catch everything, but most tools (commercial or other) are able to catch low-hanging fruit. I’m a strong advocate of leveraging code analysis and XSS vulnerability detection tools first. Fix valid issues in your tools (and real-world false positive rates are critical in determining which tool will provide the highest percentage of valid issues). This will reduce the scope of manual testing.
Focus testing on page elements which render data
In testing for XSS vulnerabilities, it’s important to remember that you’ll find vulnerabilities where data is played out (used to render HTML) that has been inputted by the user session. This is the most helpful method of scoping your testing. Focus testing on page elements which render data. Unfortunately, that inputted data can have numerous sources: user input (text controls, for instance), URL parameters, session cookie parameters, session headers, etc. Don’t limit your testing to just basic input boxes. For instance, I’ve found XSS vulnerabilities where code was keying off the value of checked radio boxes. Keep in mind that a user can proxy the Web browser and modify the value of the radio box control and inject malicious values.
As with all injection testing, several helpful tools exist. There are a number of great tools for proxying browser requests. These tools simplify the modification of request elements and allow you to generate malicious input.
Test both reflected and stored data
Remember also that inputted data takes two forms: reflected and stored. Some inputted data never makes it to the database, and developers are often less stringent with how this data is used. This data is reflected -- rather than being stored in a database, like a user’s first address line, the data is used in generating the server’s next response. Query string parameters are often a great example of reflected data. Take, for instance, a query like http://www.mysearch.org?query=”my query string” Some enterprising developer might think it’s a great idea to code the response page to include “Your search for <querystring> resulted in 157 results.” If the data used to populate <querystring> comes from the query request parameter, this is a great source for injection! I believe reflected vulnerabilities are more difficult to find, simply because reflected data can be found everywhere in a web application (there’s no tell-tale call to a data layer involved).
Stored XSS takes on most of the same attributes as reflected, with one caveat: this data is stored in a database and potentially played out a number of times, whereas reflected attacks are characterized by their transient one-time nature. Stored XSS can be more easily discovered -- find any time where data is written to the database, and track back that data to its source. When testing, consider common data which should be stored and retrieved, and focus your test efforts on the input methods for these data points.
Use your tools correctly
The good news is that there are a number of support technologies available today, which aid the developer in preventing injection vulnerabilities like XSS. But these technologies only work if they are leveraged in code and if they are configured correctly. One critical test, therefore, is to ensure that any anti-XSS technologies are properly applied. In the .NET world, for example, all IIS servers can run URLScan. This feature scans incoming URL requests searching for potential injection vulnerabilities. URLScan can be complex and misconfiguration happens often. Be sure your team takes the time to enable anti-XSS technologies, and to add test cases to validate that the functionality is properly configured.
I won’t lie to you. The scope of testing for XSS vulnerabilities can be vast. But by utilizing good engineering practices, tools to cover low-hanging fruit, and smart testing skills, you can tackle the challenge of XSS testing.