The main thing to worry about is users injecting Javascript – cross-site scripting (XSS). Other vulerabilities people used to talk about – trusting user input and checking for SQL injection attacks – are boring/easy.

If I have an XSS hole, I can steal your users’ cookies and log in as them, show a fake phishing page, embed malware, etc. And any service your site provides, I can perform it as if I was one of your users.

Reflected XSS – I embed my JS in a link to your site and trick your user to follow it.
Persistent XSS – I get my XSS onto your site’s database (e.g. by adding it to a forum).

Examples of XSS from facebook, youtube, google groups, live search. See xssed.com – sites that were xss’d.

Preventing XSS

Use a tool that escapes everything on output. Better than escaping on input because you miss out on stuff.

IE8 has an XSS filter – if scary Javascript in the URL matches that in the page, it’s blocked. But as developers, we have to assume older/other browsers.

httpOnly cookies also not so useful because many attacks don’t rely on attacker seeing cookie.

HTML sanitisation. MySpace and LiveJournal have tried to do it and been XSS’d (samy is my hero). Must be really careful because many libraries have at least one hole. UTF-7 hole which Google’s 404 page suffered.

CSS is also vulnerable. HTC in IE and XBL in Mozilla – both vectors for Javascript attacks.

Cross-site Request Forgery (CSRF)

Happens if you have unRESTful links. app.example.com/delete.php?id=1 But what if you’re RESTful and use POSTs, are you safe? No. Page B can present you with a form that submits to Page A. The form doesn’t even have to be visible – hide it and write some Javascript to submit the form when the user loads the page. Was used to get users to silently digg the page you’re on. Worse, was used in GMail to set filters so mail would be redirect to the evil guys.

To prevent CSRF:
– Could check HTTP Referrer, but that’s notoriously unreliable. e.g. there are virus protectors that strip the outgoing referrer headers, as well as FF extensions etc.

So the bettter solution it to embed crumbs (tokens). Embed an impossible-to-guess crumb in the form, and check the crumb each time you accept the form. Crumb is specific to the user and could be a cookie value, in the simplest case. Ideally, on every request and living for a short time (e.g. one hour). Must ensure crumb doesn’t leak.

Another solution with Ajax – X-Requested-By: XMLHttpRequest tells you its an XHR call, which means it came from the same domain. So if you see this, no need for the crumb check.

Plugins:

crossdomain.xml. Used by Flash to control which services can be acessed. There are various tricks will bypass it.

The PDF hole. Jan 2007 – turned out Adobe PDF readers could execute any Javascript. So Yahoo! employees for example were told to delete every PDF on the server – because having any PDF file made the whole site vulnerable.

Securing JSON

Google Contacts exposed contact data as JSON. If malicious pages makes a JSON call for that data, and you’re logged in, it will get your contact info.

But even regular JSON is vulnerable. Possible to redefine array constructor so that when JSON arrives in the browser, malicious code is executed. So JSON should look like: /* { "json" : "goes here" } */ to ensure it’s not executed. (Joe Walker makes a comment at the end of the talk that it’s safer to use while (true) or throw an exception – the JSON content itself might contain a closing “*/”.)

The window.name technique is a great alternative to JSONP because it is secure for the consumer and allows the provider to authorize access. Dojo uses this for a full framework for loading untrusted widgets.
Also, for JSON, if you are using proper authorization (like tokens), hijacking is impossible, but if you insist on relying on the browser to provide security for you, it is better to prefix your JSON with {}&& rather than \* or while(true), because it doesn’t require any stripping in the JSON eval, but is still an invalid script on it’s own.

Another solution with Ajax – X-Requested-By: XMLHttpRequest tells you its an XHR call, which means it came from the same domain.
You must never rely on HTTP header fields as security measurement.

httpOnly cookies also not so useful because many attacks don’t rely on attacker seeing cookie.
Plus you can read out httpOnly cookies via XHR

Crumb is specific to the user and could be a cookie value, in the simplest case.
Don’t encourage users to use solutions that can be easily circumvented. Also one single XSS makes ANY token based protection useless on the whole site (domain).

Simon suggests making the crumb an MD5 hash of the session id. Obviously though an attacker could just get a session id by loading any page, and MD5 it themselves. In other words, an MD5 hash of the session id is very guessable.

A better way is to use a MAC = Message Authentication Code. This is a hash that uses a cryptographic key. There are many kinds of MACs, but a simple and very effective is just to encrypt your input with a block cipher and use the last block as your crumb (a session id is so small you could use the whole cipher text, not just the last block). Now an attacker can’t figure out what your crumb is unless she knows your encryption key (which you’ll keep on the server, not on the page). To minimize replay attacks, change the key frequently.

For more information, see Bruce Schneier’s classic Applied Cryptography (although many MACs were invented after the last edition, including HMAC). A perfectly good block cipher to use would be RC2.