XSS falls into three high-level categories: reflected, persistent/stored, and DOM-based. These different categories are due to different delivery methods and impacts, even though the injected script and exploitation results are the same. Let’s break down the key differences:
Reflected Cross-Site Scripting (XSS)
Delivery Method: Requires some type of social engineering to transmit an affected link/URL to client or coerce a client into visiting an infected web page.
Impact: Code is executed in client’s browsing context. For session hijacking or other application-specific exploits, the victim must already be authenticated to the target application such that they have a session cookie issued.
Example: I send you the link www.google.com?search=test<script>alert(document.cookie)</script> via email and when you click on it, your session cookie is displayed in an alert box in your browser. In reality, an attacker would not display this content but send it via an HTTP request to one of their web servers.
Persistent/Stored Cross-Site Scripting (XSS)
Description: With persistent/stored cross-site scripting, malicious script can be stored within the application somehow (usually in the back-end database) such that when another user browses to a page that displays that variable, the code is executed in that user’s browser. This is sometimes referred to as a watering hole attack, as any user that visits that particular “hole” is compromised without any interaction.
Delivery Method: Different from reflected XSS, the malicious code in this case is stored within and delivered from within the vulnerable application. No victim interaction required and multiple victims can be compromised from a single exploited location within an application.
Impact: Usually higher impact than reflected for multiple reasons:
- Social engineering and out-of-band delivery is not required for exploitation
- Victim interaction is not required
- Multiple victims can be compromised by a single attack
- Victims will usually already be authenticated to the application when they are exploited
Example: The most often seen example of this is when a user can type <script>alert(“You’ve been pwned!”)</script> into a comment box, post the comment, and their message pops up for every subsequent user who visits that comment page.
DOM-based Cross-Site Scripting (XSS)
This somewhat newer variant of XSS (it was first documented in 2005) is sometimes referred to as client-side XSS. This variant occurs when untrusted user input is incorporated into the document object model (DOM) of a page, rather than being included in the page response from the server. At the end of the day, it is still very close to reflected XSS in that the malicious payload needs to be delivered to a victim and the root cause is due to input that isn’t properly validated and then directly incorporated into client-side script. OWASP has a very basic example of this you can check out here.
On the bright side, it is becoming easier and easier to secure new applications from cross-site scripting vulnerabilities. Many new language frameworks build in protections that automatically encode all untrusted output, like Python Flask for example. ASP.NET has request validation controls that help filter malicious content before it is processed by the application server. Additionally, there are built in functions in most languages that help support proper output encoding such that text isn’t rendered in an executable format.
But in addition to output encoding, strong input validation is also important. For XSS just like all other flavors of injection attack, any way that you can limit the malicious content that is processed by the server will improve its security posture. Client-side and server-side input validation has to be used that allows a whitelisted set of values (where possible), validates the data type submitted (e.g. no strings in an integer field), checks the acceptable content length (maximums on fields in the user interface that match variable lengths set on the back-end), and filters out unwanted characters (where possible).
Finally, if you’re looking for some additional protection against these kinds of attacks, you can always consider a Web Application Firewall (WAF) to provide some defense in depth. These devices are pretty good and blocking a lot of the low hanging fruit when it comes to malicious content associated with injection. And while they are not a silver bullet and can likely be bypassed given enough time, a WAF will certainly help raise the bar for your website security and make it more difficult for an attacker trying to compromise it.
As always, if you want to explore having a web application penetration test conducted to see if you are vulnerable to any XSS flaws or if you just have questions about this type of vulnerability in general, feel free to reach out!