This blog covers Cross-Site Scripting (XSS) vulnerability from a different perspective. Generally, XSS is when the application takes user supplied JavaScript and displays it without escaping/encoding. In this blog, we will see how can XSS be exploited even if the application properly escapes/encodes the user inputted JavaScript using different methods. Exploiting XSS in this way can be as drastic as exploiting XSS in the regular way. Using this, an attacker can carry out attacks against the application users such as stealing cookies, creating a Trojan login. This is a regular XSS i.e. reflected XSS, but there are other types of XSS such as Stored and DOM based XSS.
Need to include cross domain resources:
The ever growing need of giving a rich user experience to website visitors have made the need for browsers to include cross origin resources. Sometimes these resources can be data, an iframe, an image or JavaScript.
For example:
A website http://example.com can have the following cross origin resources:
- Data from website https://securelayer7.net of the logged in user using CORS specification using cross domain XMLHttpRequest. More about it here: http://blog.securelayer7.net/owasp-top-10-security-misconfiguration-5-cors-vulnerability-patch/
- An iframe from https://youtube.com using <iframe src> tag
- An image from http://imgur.com/ using <img src> tag
- JavaScript library from https://code.jquery.com/jquery-1.8.1.min.js using <script src> tag
Why do we need external scripts and why not refer internal scripts?
They enhance the performance of website as they are cached by the browser from the CDN.
Referring the following scripts will improve performance: <script src=”https://code.jquery.com/jquery-1.8.1.min.js”></script> because multiple sites use the same and the browser has to load it just once from the CDN and after that each time it is referenced, it is served from the browser cache.
If instead of the above, a website refers scripts internally. That is, downloads the jquery-1.8.1.min.js file and serves it from its own root directory like <script src =”http://example.com/jquery-1.8.1.min.js”></script> then it will cause browsers to download load scripts from the internet instead of using it from the browsers cache that is downloaded from CDN as a result of which the performance is affected.
We need external scripts because they allow developers to write less code and let them make DOM manipulations easy.
The following is a jquery’s single line of code to change background colour:
$ (‘body’) .css (‘background’, ‘#ccc’);
In JavaScript, the code to perform the same functionality is:
Function changeBachground(color) {
Document.body.style.background = color;
}
Onload=”changeBackground (‘red’);”
So, 1 line of code in JQuery is equivalent to 4 lines of code in JavaScript when it comes to changing background colour.
Let us see the security vulnerabilities that arise due to bad JavaSript imports:
Loading scripts from third parties cause security vulnerabilities because the provider has complete control over the content served on the web page. A malicious website can serve malicious JS to a victim site thus doing a Cross Site Scripting attack. Research has leaded us to find the following types of mistakes that cause vulnerabilities:
1. Script inclusion from localhost
Suppose in a development environment (for testing purpose), a developer references scripts from another web server running on localhost. For example: <script src=”http://127.0.0.1:4545/import.js”></script>
If this code of the web app goes in production then, in this case an attacker can exploit it in the following way:
- Taking access of a computer
- Starting a web server on localhost at port 4545 serving the malicious JS.
- Attacker leaves the computer and victim uses it.
- Victim browsers the vulnerable website.
- The website launches and loads JS from the address http://127.0.0.1:4545/import.js and it results in XSS.
2. Script inclusion from Private IP from intranet
Suppose in a development environment (for testing purpose), a developer references scripts from another web server running on a private IP address of the intranet. For example: <script src=”http://192.168.0.111/import.js”></script>
If this code of the web app goes in production then, in this case an attacker can exploit it in the following way:
- Taking access of a computer within the intranet that has IP: 192.168.0.111.
- Starting a web server on it serving a malicious JS.
- Victim browsers the vulnerable website.
- The website launches and loads JS from the address http://192.168.0.111/import.js and it results in XSS.
3. From Non-Registered domains
A developer references scripts from a domain on the internet. For example: <script src=”https://securelayer7.net/import.js”></script>
But due to some reason, in future, this domain expires. Then, in this case an attacker can exploit it in the following way:
- Registering the domain securelayer7.net and serving malicious JS.
- Any victim user that uses the website from a remote location will load the script from the newly registered domain and it results in XSS.
4. From non-static IP
A script is referenced from an IP address instead of domain name. This sounds good as long as the IP address is static. For example: <script src=”https:// 184.75.249.44/import.js”></script>
But if the IP address is subject to change, then, in this case an attacker can exploit it in the following way:
- Attacker gets hold of that IP and serves malicious JS
- Any victim user that uses the website from a remote location will load the script from the website on the old IP and it results in XSS.
5. From typing mistakes in domain names
A script is referenced from correct domain but with some typing mistake. For example: <script src=”https://code.jqueri.com/import.js”></script>
Notice that in above example, the spelling of JQuery contains ‘i’ in the end instead if ‘y’. In this case, an attacker can exploit it in the following way:
- Attacker registers a domain with the incorrect name i.e. code.jqueri.com and serves malicious JS.
- Any victim user that uses the website from a remote location will load the script from the newly registered domain resulting in XSS.
6. From a server serving content over HTTP (This vulnerability has now been patched in all modern browsers)
A script is referenced from a server that serves content over HTTP and NOT in HTTPS. For example: <script src=”http://example.com/import.js”></script>
Notice that the domain name is correct without any spelling mistake. In this case, an attacker can exploit it in the following way:
- Being in a suitable position on the network.
- Performing a man in the middle attack and changing legitimate JS to serve malicious JS resulting in XSS.
Mitigations:
Use of Sub-resource Integrity feature of browsers:
The Subresource Integrity feature enables to mitigate the risk of these attacks, by ensuring that the files Web application fetches (from a CDN or anywhere) have been delivered without a third-party having injected any additional content into those files — and without any other changes of any kind at all having been made to those files. It is done by using the Subresource Integrity feature by specifying a base64-encoded cryptographic hash of a resource the web application is telling the browser to fetch, in the value of the integrity attribute of any <script> or <link> element. For example:
You can use the following <script> element to tell a browser that before executing the https://securelayer7.net/import.js script, the browser must first compare the script to the expected hash, and verify that there’s a match.
<script src=”https://securelayer7.net/import.js“ integrity=”sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC” crossorigin=”anonymous”></script>
When a browser encounters a <script> or <link> element with an integrity attribute, before executing the script or before applying any stylesheet specified by the <link> element, the browser must first compare the script or stylesheet to the expected hash given in the integrity value.
As of now, it is supported by Mozilla Firefox, Google Chrome and Safari.
Apart from this following measure should be taken to secure your JS imports:
- The code (in development environment) which serves scripts from localhost or any private IP address should be replaced with secure code that serves scripts from secure location when the code is deployed in production.
- Monitoring of the existence of the domains should be continuously done in order to make sure that script is not referenced from a domain which is expired.
- Scripts should not be referred from a non-static IP address.
- Typing mistake should not be done while referencing scripts.
- Scripts should only be served from servers which have implemented HTTPS.
- Scripts should be served from a domain which you trust and which don’t have a RCE vulnerability. As it can lead to an attacker replacing the script that victim site references with malicious JS resulting in XSS in victim site.
- If you are unsure of the above point, then prefer to save the scripts on your own server rather than external reference.