The 'key plus authenticator' pattern in web apps

August 10, 2011

As part of our account request system, I need to generate an unguessable URL for a particular account request. This is a widely solved problem, because it's a variant on session IDs; you generate a large random number, encode it to ASCII somehow (base64 is popular), and put it in the URL as the identifier of the particular object. When you get an incoming HTTP request you extract that portion of the URL and use it as the key to do a database lookup to find the record you want.

However, there's an issue. Database keys have to be completely unique, but large random numbers are merely almost certainly unique (cue the birthday paradox again). Since I do not enjoy debugging weird database errors that happen once in a blue moon, I decided that I wanted to do database lookups with something that was guaranteed to be unique; in this case, a primary key. But I still wanted the URL to be unguessable.

My solution was to use what I'm calling a 'key plus authenticator' pattern. I embedded in the URL both the account request's primary key (which is unique but easily guessed) and a large random number (encoded to base64) that I call the access hash. When a URL comes in I extract both pieces of information, use the primary key to look up the account request, and then check that the account request has the right access hash.

An attacker can easily guess potentially valid primary keys but they can't get access to anything unless they also get the access hash correct (they can't even tell whether or not the primary key exists; I return an identical 404 error in both cases). With enough bits in the large random number this is just as secure as using the large random number alone. I call this the 'key plus authenticator' pattern because demonstrated knowledge of the access hash has served to authenticate your knowledge of the guessable primary key.

(You may well want to store the authenticators in your database in some hashed form, for the same reason as session IDs. We don't do so in the account request system for a number of reasons, including that I only found out about the session ID issue after I'd written the system.)

Comments on this page:

From at 2011-08-10 07:39:04:

I have used HMACs for this in the past. They save me having to store the access keys. I just have one server-side secret and use it to generate easily verifiable URLs.

From at 2011-08-14 12:04:28:

That's an interesting idea, but why not just AES 128 encrypt the auto_incrementing index, then base64 encode that? Then you can decrypt it on the other side and look it up in the database.

-- Eric Gerlach

Written on 10 August 2011.
« You need to hash web app session IDs
Friendly 'noreply' email addresses »

Page tools: View Source, View Normal, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Wed Aug 10 01:29:18 2011
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.