— A post from Damien Sawyer, Senior System Architect at AgentPlus and Run Property. Enjoy! —
I’ve been working with B2cloud in recent weeks on an iPhone app. It’s a standard client/server affair. B2C is writing the client while my job is to expose our database to the world via a JSON webservice. It is our intention that, while the iOS client will be its first consumer, the webservice will hopefully pave the way for future SOA efforts (if you haven’t read the recently ‘leaked’ google plus post on the topic, its a great read. I think the original has been pulled, but there’s a copy here).
We weren’t too far into the design process before Tom (B2Cloud) and I found ourselves in a debate about security. The basic plan was that the client would make first contact by calling a login function, passing a memberID and password. On the server, I’d validate the combination and, on success create a row in a database table that had a GUID (aka UUID) as a primary key. This “session key” would be symmetrically encrypted and base 64 encoded before being sent back to the client.
What would happen on subsequent requests is where Tom and I differed in opinion. Tom suggested that on each subsequent function call, in addition to the ‘payload’ data required by that function, we pass both the session key and the memberID back for authentication. The thought was that, although it was very unlikely that a duplicate session key could ever be generated (according to wiki, “generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%.”), there was a chance that one could.
Should a duplicate key be returned, then a client could be authenticated against someone else’s account and … well, without being overly dramatic, the entire Earth would implode. By passing back the additional memberID, this could never happen as, back in server land, I’d validate that the session key was indeed generated for that user.
Now, for reasons that I wasn’t really able to properly articulate at the time, the idea of passing back two strings for authentication with every function instead of one really grated with me. It just seemed “messy” and unnecessary. If this webservice is a success, one day it could literally have hundreds of functions. The idea of requiring an additional input field on each of those was unnecessary and unpalatable. For reasons probably more to do with my own personal neuroses rather than logic, It actually really bugged me. I suppose that, like many programmers (by no means all!) I take pride in my work. Whats more, by definition, these functions will be public. One day when we surpass Amazon.com in size, millions of people across the globe will use them every day (ok…ok… But a guy can dream) and I will be under the scrutiny of other devs for this decision.
I wanted to drop the memberID and rely solely on the uniqueness of the GUIDs for security. Tom challenged me along the lines of “what’s the big deal? By passing the memberID we’re ensured security. Why risk it?”.
For the record, I think that both solutions are acceptable. Passing an additional field really is no big deal and the chance of a duplicate GUID are acceptably improbable. If I had a balanced work/life perspective …. I probably would’ve dropped it all here. But, alas, I don’t and I didn’t.
The thing is though, something else was really bugging me. As hard as it was for me to admit at the time, Tom was right. Even if it was one chance in several trillion that my proposal would fail, near enough wasn’t good enough.
Once I accepted that, another thing bothered me. I admit that this discussion so far really has been making a fuss over nothing. However this next point I believe is worth exploring more.
I didn’t like the idea of the client passing two fields because it ‘feels’ like it is putting some of the onus for security on the client as opposed to the server. Now remember, even though I trust Tom and his app, this web service is public. As its creator I have to assume that clients may be malicious. Having the Client pass back a memberID just seemed like “too much information”. Once again, I know that there’s probably little chance that someone could use this to gain an advantage (however, really, what do I know about subverting security systems?), but it’s the principle of the thing. It just seemed inelligant.
In the end, Tom and I agreed on a solution. The session key that I returned would be the GUID concatenated with the memberId and then encrypted/encoded. That way, each key could only ever access one account (unless in the future we decide to embed more info in the key and extend the functionality…. which is purely a server side matter) and we don’t have to require an additional unnecessary field. Win win.
In summary, I’m taking away the following learnings from this experience.
- When you’re building a web service – the server is boss. What’s more, It appears to me to be good practise to keep the exposed ‘surface area’ of your functions to the minimum required. Perhaps this has a bearing on security (or perhaps it doesn’t!) however, at the very least, it will keep your interfaces ‘cleaner’.
- Debate is good. There were quite a few emails back and forth about this one which lead to a solution which I believe is better than the original ones proposed.
All the best and happy new year 🙂