I have some Ruby web apps that use OpenID for authentication and store the session in a cookie. There are a few API- and AJAX- related things that my Ruby frameworks aren’t a good fit for, so I’ve got some node.js services. The problem is that if someone knew the URLs of my AJAX services, they’d basically be open to the public as things stand. At the moment those services do a simple check of the Origin header, but obviously that’s very easy to forge.
So I want to be able to restrict access to the services running on Node (or Python, or in a non-Rack based Ruby service, or anything else) to users who are logged into the ‘main’ service that’s run through a Rack-based web application. Are there any conventions for how this sort of thing is done? I’ve seen heaps of websites that will serve content and pages through example.com, and then the AJAX calls get made through api.example.com, so I’m surprised this is something I’ haven’t read about.
I do have an idea for how to do this, and I’d love some feedback on whether I’m missing something blindingly obvious that makes this insecure:
My Ruby web app uses OpenID for authentication and stores the session in a session cookie using Rack::Session. From looking at the Rack::Session source, my framework seems to go through this process:
- generate a Marshal dump of my User object
- generate a SHA1 hash of the Marshal based on a secret key
- store a hex digest of the SHA1 hash in a cookie
So theoretically I could have a pre-arranged key or system for generating the key, or some message passing between frameworks through a secure channel to share the key. Then I could reverse the encryption process in any other framework that wanted to be able to verify session data. I’d have to get rid of the first step and only store JSON data or something instead of a Ruby object for cross-language compatibility, of course.
Is this considered a secure way to do things, assuming the protocols for sharing the key are appropriately secure?
What you’re describing is a Message Authentication Code (MAC); in this case, it’s a Hash-based MAC or HMAC. Basically, take a representation of the data you want to authenticate (make sure is coming from a certain source), append a secret key to it, and hash the whole thing. Then attach that computed hash to the message (what you just hashed minus the secret key). When the receiving party receives the message, it would take the data, append the same shared secret to it, and hash it. If that computed value is the same as the one received as part of the message, it is authentic and should be processed; if the hashes do not match, it is not from the party it should be from and should be discarded.
You may want to look at the RFC specifying the HMAC construct (just don’t use the sample code as it still uses MD5; use something like SHA-256 or SHA-512 to implement your HMAC):
http://www.ietf.org/rfc/rfc2104.txt