Technical Insight-True Stories of the TRC-Vulnerabilities

Web Storage Security

The web waits for no one, not even W3C.

While the HTML5 specification isn’t finalized, and HTML5 Storage has even been broken out into its own Web Storage Specification, which is even further from being finalized, code continues to move to the client and more developers are (mis-) using the next generation features that are already available in the browsers. Engineers and researchers in the WhiteHat Security Threat Research Center are in a unique position to know “where there is code, there are vulnerabilities,” and JavaScript is certainly no exception.

Over the past few months, the Threat Research Center has implemented new checks into WhiteHat Sentinel to better identify and analyze the usage of Web Storage and its potential security impact.  During the course of this research, I analyzed over 600 applications that made at least one call to Web Storage — “getItem” or “setItem”.  The preliminary results may surprise you. They sure surprised me.

Before I jump into the vulnerability discussion, a brief word about some of the so-called “security advice” concerning HTML5 APIs and specifically Web Storage. I’ll be the first to admit that before this project I’d never used the Web Storage APIs; this was a from-scratch effort. Like any good developer (or hacker) learning a new technology, I googled “Web Storage Security”, “localStorage Security”, and “HTML5 Storage Security”. The results were somewhat discouraging.  I couldn’t find a single vulnerable code example and most security commentaries boiled down to either “there is no major risk” or “if developers use Web Storage properly there is no risk.”

The argument is that because stored values aren’t transmitted over HTTP we actually have a more secure option for storing data that is only needed on the client. In my opinion, this is just flat out wrong.  Arguing that “if developers use it correctly there is no risk” is like saying “if PHP developers use $_GET correctly there won’t be any problems.” We all know how that turns out.

So I knew I needed to go to the source. Section 7 of the Web Storage Specification is titled “Security”; certainly we can get some good advice here. Honestly though, I found section 7.1’s warning about DNS spoofing attacks and 7.2’s warning about cross-directory attacks to be a bit hollow. I’m not saying these attacks don’t exist, but certainly we can give some better advice than “Use TLS” and “Don’t implement on shared domains”. Section 7.3, on implementation risks, appears to be entirely targeted at browser makers. If developers can’t go to W3C for advice on how and how not to use Web Storage securely, then where can they go? It looks like we are back to Stack Overflow and random blog posts. With that being the state of security advice on Web Storage, I figured I’d throw my hat in the ring.

Examples of Vulnerability:

Evil Roommate / Public Computer

Firefox’s about:home page is vulnerable to DOM (document object model) XSS via localStorage injection through the snippets functionality. While I can’t send you a link or build a malicious website to exploit this issue I’m willing to bet that thousands of people use FireFox on a shared or public computer every day. It sure would be nice to be able to log all of those keystrokes even after the browser is closed and private data has been cleared.

Screen Shot 2013-05-18 at 5.12.34 AM

Just sit down and run a weaponized version of the following bookmarklet:

javascript:window.localStorage.setItem(‘snippets’,'<iframe src=”” onload=”prompt()” style=”width:100%;height:100%;z-index:9999999;position:absolute;left:0px;top:0px;”/>’);

When contacted about the above issue via email the Mozilla security team advised that they are migrating the functionality off of localStorage for reasons other than security. Even so, I’ll be keeping my Firefox usage to my own computer that is always locked whenever I am not using it. At least until this functionality is patched.

DOMXSS –> localStorage XSS –> The persistent vector your sever will never see.

Vulnerable Code:

<script language=”JavaScript”>

var Id = getPramValue(“id”);

var persistId = localStorage.getItem(‘id’);

if( isValid(Id) ){

document.write(‘<a href=”’+ Id” id=”store_locator”>’);

document.write(‘<div>Find Store</div>’);


} else if( localStorage && isValid(persistId)) {

document.write(‘<a href=”’+ persistId” id=”store_locator”>’);

document.write(‘<div>Find Store</div>’);


}else {

document.write(‘<a href=”” class=”scroll linktomap” id=”store_locator”>’);

document.write(‘<div>Find Store</div>’);




Proof of Concept:

<a href=”'”><img/src=”x”onerror=eval(String.fromCharCode(119,105,110,100,111,119,46,108,111,99,97,108,83,116,111,114,97,103,101,46,115,101,116,73,116,101,109,40,39,105,100,39,44,39,34,62,60,105,109,103,47,115,114,99,61,92,34,120,92,34,111,110,101,114,114,111,114,61,97,108,101,114,116,40,49,41,62,39,41))>

The String.fromCharCode here just makes it easier to insert the needed injection into localStorage without excessive quote escaping. Here is what it decodes to:


The Always and Never of Web Storage


Always  validate, encode, and escape user input before placing into localStorage or sessionStorage

Always  validate, encode, and escape data read from localStorage or sessionStorage before writing onto the page (DOM).

Always  treat all data read from localStorage or sessionStorage as untrusted user input.


Never store sensitive data using Web Storage: Web Storage is not secure storage. It is not “more secure” than cookies because it isn’t transmitted over the wire. It is not encrypted. There is no Secure or HTTP only flag so this is not a place to keep session or other security tokens.

Never use Web Storage data for access control decisions or trust the serialized objects you store here for other critical business logic. A malicious user is free to modify their localStorage and sessionStorage values at any time, treat all Web Storage data as untrusted.

Never write stored data to the page (DOM) with a vulnerable JavaScript or library sink.  Here is the best list of JavaScript sinks that I am aware of on the web right now.  While it is true that a perfect storm of tainted data flow must exist for a remote exploit that relies 100% on Web Storage you must consider two alternate scenarios. First, consider the evil roommate, unlocked, unattended, or public computer scenario in which a malicious user has temporary physical access to your user’s web browser. The computer’s owner may have disallowed a low privileged user from installing malicious add-on but I’ve never seen a user prevented from making a bookmark. Second, don’t ignore the possibility of improper Web Storage usage allowing escalation of another vulnerability such as reflective cross-site scripting into persistent cross-site scripting.

  • Dev

    In our paper, we investigated similar concerns:

    Artur’s talk is also fun

  • David Garza

    Hi, thanks for the article, I´m very interested on learning html5 and all its Api, I´m currently learning about local/sessionStorage and came to this blog looking for good or bad reasons to use it. How can I test my webApp (made it with MVC and HTML5) if it vulnerable to all this type of attacks or issues? I take by default that all libraries use it in the app have their own methods to keep my webApp safe but one never knows.

    Greetings and again thanks for the article.

  • David W. Keith

    “There is no Secure or HTTP only flag so this is not a place to keep session or other security tokens.”

    This is not exactly true, there is no Secure Only flag because the Web Storage is only available to ECMA Script on the same origin (unlike cookies, which predate the concept and can have access configured on save). Server only flag of course do not exist as it would require the browser acting a a server, which is a whole other can of worms with security.

    Like cookies, most browsers store Web Storage as plain text on disk, so any sensitive data stored there should be encrypted, either on the server side, or in the client, depending on the use case and requirements. (eg encoded tokens vs peer-to-peer history)

    There is no way to store access tokens other than in plain text, so it is best to create a tuple with another piece of data to make reuse more difficult (often the user agent string, but fingerprinting would be better)

  • derekgreer

    “There is no Secure flag …”

    Actually, local storage values on Secure (SSL) pages are isolated which means a page from can’t access local storage from This can be contrasted to cookies which will behave this way only if the secure flag is provided. This means that local storage is “more secure” in this particular respect.

    Both local storage and cookies each seem to have their own set of pros and cons for storage. Cookies are more susceptible to vulnerabilities that local storage is not and vice-versa. It’s somewhat misleading to say Web Storage is not “more secure” than cookies without also saying that Cookies are not “more secure” than Web Storage. In truth, neither are universally more secure than the other, but each are more or less secure with respect to certain types of vulnerabilities.