PHP

Implementing request signing in Lithium

If you are building out a somewhat successfull app you will probably at some point want to provide API access to your application. Giving API access to private data requires you to think about how access credentials should be handled, and there are multiple options to go with here:

  1. Sessions based — You can reuse any access checks you have, but as it requires the client to maintain a cookie (most often) it makes using your API a pita.
  2. Passing username / password as GET/POST fields on each request — Dead simple in both ends, but as it is a security disaster it should not be considered.
  3. Request signing — Simple to use on the client after initial implementation as you can easier fire one-off requests
  4. OAuth (or similar) — A token based access system, good because its secure and there are a lot of libraries for it. Not so simple on the client as you must maintain the token however.

For the uses we’ve had I’ve often chosen #3, a request signing approach. This works by using the arguments (url, parameters) plus an API-key to generate a hash that is passed along the request. The arguments are visible in the request and those plus the shared secret key can be reused to generate a new hash on the server side that is compared against the passed signature. If the hashes match its a valid request.

The main upside this has is that eavesdropping to get the signature of one request does not allow an attacker to reuse it for another request, except to resend the exact same request, which probably isn’t too bad.

The first thing we need to set up is a pretty straight forward user model. Apart from the usual suspects you need to have an `apiKey` field to specifically use for API requests:

The next thing we need is a very simple protected controller resource that lists users:

I’ve not implemented any login handler so without the API this resource is now not possible to access, so lets build on the Auth class in Lithium to support signed request as well as password based auth:

Could not embed GitHub Gist a9fdbf5bfb8026fee8cc: API rate limit exceeded for 192.81.221.156. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)

Now we can finally run a request against the API and see if we pass authentication by signing our request. I’ve implemented a simple Command for this:

Could not embed GitHub Gist a9fdbf5bfb8026fee8cc: API rate limit exceeded for 192.81.221.156. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)

If you run this command using li3 users consume 1 you should get the print_r listing the single user from the database, while if you attempt to access the same url in your browser it should give a Not authed response.

All of the code is found on github as a working Lithum mini app: nervetattoo/li3-request-signing

 

About Raymond Julin

Lead developer at Keyteq Labs, a product business in Bergen, Norway with a reputation for modern user friendly solutions.

3 Comments

  1. I would suggest extracting the filter code into an auth adapter.

    • Raymond Julin

      Can you use two adapter implementations for the same auth name? Both Form and signed request is in use here for two different ways of accessing the same named auth.

      • Ah, I see. I’m actually thinking about implementing a Multi adapter for cases like that, which could check a series of adapters in sequence, or based on a condition. Something like this would be a good use case.

Leave a Reply