Single Page Web App Security Cheat Sheet
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).
Whenever we are building code from strings (
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.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.
Rule: Protect your services
Browsers these days support a number of HTTP headers we can use to protect our app. These include
X-Frame-Options to avoid Clickjacking, Content Security Policy to mitigate Cross Site Scripting and related attacks,
X-Content-Type-Options to make sure the browser cannot be tricked into for instance interpreting JSON as HTML (only works in some browsers).
Rule: Learn how to use security HTTP headers
- DOM XSS Wiki
- DOM Based XSS Prevention Cheat Sheet
- XSS (Cross Site Scripting) Prevention Cheat Sheet
- Content Security Policy
Want to Contribute?
Got ideas for improving this cheat sheet? Send me an email or send me a pull request. Contributions will be attributed.