Security researcher Mario Heiderich (also creator of the HTML5 Security Cheatsheet and lead developer for PHPIDS) has been posting some interesting cross-site scripting challenges lately that highlight aspects of security on the client side. The most recent, called XSSMe², involved a page with a reflected XSS vulnerability that allowed one to insert arbitrary HTML – no filters applied by the server. The goal? Retrieve a particular bit of data, originally stored in document.cookie, without any user interaction. I say “originally,” because the page included JavaScript which attempted to lock down access to the data by removing it from document.cookie and hiding it unless retrieved by a user click. The code used evolved as bypasses were found, with several tricks employed along the way.

One trick was to hide the variable in a closure. In JavaScript, every function has its own local scope. If you define a variable within a function block, that variable is distinct from one defined in the global scope. In a way, the variable is hidden from code executed in the global scope, though the function can provide a gatekeeper method to access it. Consider this block of code:

document.cookie = "secret";

var Safe = function() {
    var cookie = document.cookie;
    this.get = function(magicWord) {
        if (magicWord === "please") {
            return cookie;
        return null;
window.Safe = new Safe();

document.cookie = "";


The first alert returns nothing – document.cookie has been set to an empty string. The second alert only returns null, given the if statement in the definition of Safe.get. But with the third alert, the statement return cookie gets executed – and that statement is in the local scope of the function, so it returns the cookie variable defined in that scope, which is “secret”. This is the concept of a closure – the local variable of the function lives on as it was defined in that context.

Initially, this may seem to be a good defense against cross-site scripting, since the power of XSS comes from all a page’s scripts executing in the same scope. But as entries in the challenge demonstrated, a script has many resources for attacking itself. For instance, the challenge included code that checked whether a function requesting the secret variable was a mouse click event initiated by the user. That last bit came from checking the isTrusted property on the event, which should tell you whether the click came from a script or from the user.

But in JavaScript, new objects are created by cloning a model object called a prototype. If you change a particular prototype, any new variety of that object will inherit the changes you made. In this case, changing the isTrusted property of a mouse event’s prototype to always be true meant any spoofed clicks generated automatically by a script would fool the protective code and retrieve the secret value.

With each new bypass, Mario updated the code with new protections to block them. Eventually, he created a Firefox-specific version that essentially rewrote the entire page to get rid of the original Document Object Model and all its loopholes. If you’re interested in reading more about other bypass techniques and the challenge’s implications for client-side filtering, researcher Krzysztof Kotowicz has an excellent write-up that covers more details. But the challenge is also worth studying as a way of understanding more about web scripting and XSS. I certainly learned more about closures and event spoofing by tackling the puzzle, and it helps illustrate the difficulties of trying to protect against code running in the same origin and same scope. We may be moving towards DOM features that provide enough security to block even client-side attacks, but for right now, any untrusted script has myriad ways of overcoming client-side protections.

One thought on “Can Client-Side JavaScript Protect Itself?

  1. Pingback: Marcus Pontin

Comments are closed.