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.
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.
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 aware of 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.
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
- OWASP Top 10 for JavaScript
- DOM XSS Wiki
- DOM Based XSS Prevention Cheat Sheet
- XSS (Cross Site Scripting) Prevention Cheat Sheet
- Content Security Policy
- Clickjacking
Got ideas for improving this cheat sheet? Send me an email or send me a pull request. Contributions will be attributed.
Erlend Oftedal - @webtonull / eoftedal