Single Page Web App Security Cheat Sheet

This is intended as a simple abbreviated cheat sheet for securing JavaScript based single page web apps. It's not meant to cover everything in depth, but rather point you in the correct directions with resources.

What happens in JS, stays in JS

Or to be more precise: What happens in the browser, stays in the browser. We cannot make security decisions for our data within the browser. Any input validation (whether in html tags or implemented in JS) in the browser is purely cosmetic and for user guidance. Any security decisions based on data within the browser, need to be double checked on the server. Anything happening in the browser can be altered. An attacker can access your services directly, thus circumventing any security implemented in the browser. In Sverre H. Huseby's excellent book "Innocent Code" this split between server and browser is referred to as the Invisible security barrier.

Rule: Access control, input validation and security decisions must be made on the server.

Cover your XSS

Cross site scripting is a serious vulnerability. Even though XSS is often demonstrated using simple alert boxes, XSS is a common vector for delivering exploits. Consider using XSS to add an applet tag pointing to a malicious Java application. Game over (because I know you forgot to update to the latest Java).

We need to escape untrusted data whenever we are outputting data in our templates. And we need our escaping to be awareof context.

Whenever we are building code from strings (eval, new Function, setTimeout, setInterval), we need to be really careful. Escaping quickly becomes very difficult, so it's better to just avoid it. JSHint says "eval is evil", and I agree.

Untrusted data can come from so many places. Some examples are URIs, JSON services, window.referrer, window.name, input fields, cookies. And there are lots of sinks that output raw HTML into the DOM, and can thus result in JS execution.

Rule: Handle untrusted data with care — use contextual encoding and avoid building code from strings.

You have been served

In a single page web app, private data only exists in the app's JSON services, thus it goes without saying we need to protect these services. We need to make sure authentication and authorization (access control) is properly implemented. We need to make sure we treat incoming data correctly — taking into account character sets, content-type, input validation. We need to make sure we don't expose unexpected data (e.g. a user's password hash). And we need to avoid mass assigment — allowing an attacker to change fields we don't expect to be changed. We need to avoid things like CSRF.