Nonce - It’s like the deli counter

I was just perusing Douglas Crockfords post from yesterday, it’s a calming voice of reason in the current JSON and AJAX is s security vulnerability witch hunt that seems to be this quarters tech blog writers topic of choice.

Anyway, it got me thinking about nonces. Yes that’s right nonces. And after posting a comment on Douglas’s blog I felt the need to go into greater detail here.

What’s a Nonce?

A nonce is a single use key. Often used in cryptography and authentication…

It is often a random or pseudo-random number issued in an authentication protocol to ensure that old communications cannot be reused in replay attacks. For instance, nonces are used in HTTP digest access authentication to calculate an MD5 digest of the password.

http://en.wikipedia.org/wiki/Cryptographic_nonce

This is relevant to what exactly?

Well the approach I’ve been thinking would be nice for CSRF vulnerability fixing would be to somehow authenticate a request on multiple levels.

First you are able to authenticate the user and their browser as being the initiator of a a request, because the cookie can be validated as belonging to that person.

The issue with CSRF is that although the cookie authenticates that an authorized user is sending that request, it does not authenticate what domain they are on when they initiate that request. We need to therefore authenticate the page the user is on when the request is sent.

So going back to the great good.com vs. evil.com example.

One approach would be to have every form that is rendered as a good.com web page, include a secret key that would be passed back to the server that the server could use to verify that the page doing the request is in fact a good.com page.

If you just used the same key on every page, you might run the risk that evil.com people could login using their account, find the key and fake it when they do their CSRF attack by just including it in the parameters they pass in the request. Good.com will think it’s a legit request and respond - we’re back at square one.

So the idea I had was that you use a nonce. A single use key that is specific to the good.com, specific to that user, even specific to that session.

If you will, when a person logs into good.com, they would not only have a session cookie set, but would also have a key created, every form that is rendered from that point on, and every JavaScript XHR handling methods would have this key added to them. When a form or an XHR request is made to the server one of the included parameters would be this key. The server is then able to authenticate that the user is who they say they are (due to their use of their browser and the cookie associated with it), but that they have also initiated that request from a page that must life within the good.com world. Once a key has been submitted it is checked off as used, and a new key created. That new key is then used in all subsequent forms that are rendered (as a hidden field) and/or if the request is via XHR, the new key is passed back in the JSON response.

Nonce-Figure-1

As the whole request -> response system should work, you should never have a situation where XHR requests are being submitted without an expectation of a response. You could for example have a central response handler function that part of it’s role would be to take that new key and redefine a ‘global’ userKey variable that all XHR requests use - it could also via something like a unique classname, also repopulate all the hidden form fields on a page with the new key (in the case of pages that have mixed XHR and Traditional request methods.

Now Serving

What’s does this have to do with the Deli Counter

Warning, bad analogy time.

When you arrive at a deli counter you’re asked to take a ticket. Once the counter reaches your number you had the ticket back to the server (who throws it away) and they serve you. They then move onto serving the next person in the who’s ticket matches the number of the digital screen.

If someone comes along and tries to jump the line (queue) they can’t unless they have a ticket.

If they manage to get hold of someones old ticket, they can’t use it, as it’s already been used and the digital counter has moved on.

To further extend the analogy, you then make every customer sign for the ticket when they take one, and you then check not only that there ticket matches but also make them sign again to check that their first signature matches the one they have before they get served.

Taking it to a silly level, you’d only allow users to get a ticket when they walked in the front door of the store (that they have to sign for). This then prevents someone climbing in a window and trying to forge your signature to get served. Because they didn’t come in the front door, they don’t have any access to the tickets, so they have no way of jumping ahead of you and ordering the last of the Bologna.

Why this isn’t the fix all solution

The A in AJAX stands for Asynchronous. One issue you would have with the approach above is in applications where requests via XHR to the server are in fact occurring in a genuinely asynch fashion. What that means is, if you have an application where I might be sending an XHR request, and then sending a second XHR request before the first one has responded and my key been updated, my second request would fail because it would be using the first key, which by the time it gets to the server is out of date and it’s use considered an attack. If you’re using true asych XHR you’d need to look for a subtle variation of this approach (one that I have yet to work out - I’m open to suggestions).

JSON web services would also be an issue. But in all honesty if you have a web service of any type that you want secured and you’re only relying on a cookie to authenticate you’re in a whole heap of trouble anyway. Web services assume that me over at mine.com can access a url on good.com and get JSON back that could be viewed on my site. If you are doing this, and are trying to find a solution for CSRF you’re looking in the wrong location as your security model would, you should really be doing some sort of stateless per request authentication using https and usernames and passwords, or an S3 authentication model (of stateless time dependent nonces).


About this entry