Am working with phil sturgeon REST_Controller for codeigniter to create a REST api, so far i've been able to create a simple library for generating api keys for the users. My problem is now sending the api key to the API for each request, how i do this without having to manually send it for every request.
2 Answers
You should look into request signing. A great example is Amazon's S3 REST API.
The overview is actually pretty straightforward. The user has two important pieces of information to use your API, a public user id and a private API Key. They send the public id with the request, and use the private key to sign the request. The receiving server looks up the user's key and decides if the signed request is valid. The flow is something like this:
- User joins your service and gets a user id (e.g. 123) and an API key.
- User wants to make a request to your API service to update their email address, so they need to send a request to your API, perhaps to
/user/update?email=new@example.com
. - In order to make it possible to verify the request, the user adds the user id and a signature to the call, so the call becomes
/user/update?email=new@example.com&userid=123&sig=some_generated_string
- The server receives the call, sees that it's from userid=123, and looks up the API key for that user. It then replicates the steps to create the signature from the data, and if the signature matches, the request is valid.
This methodology ensures the API key is never sent as part of the communication.
Take a look at PHP's hash_hmac() function, it's popular for sending signed requests. Generally you get the user to do something like put all the parameters into an array, sort alphabetically, concatenate into a string and then hash_hmac
that string to get the sig. In this example you might do:
$sig = hash_hmac("sha256",$params['email'].$params['userid'],$API_KEY)
Then add that $sig
onto the REST url as mentioned above.

- 92,731
- 24
- 156
- 164
-
1Just trying to understand this because I am trying to do something like this...`/user/update?email=new@example.com&userid=123&sig=some_generated_string` now couldn't someone hijack `some_generated_string` and send that as if it were from themself? (I'm sure they can't but maybe you can tell me why?) – JasonDavis Dec 22 '11 at 14:20
-
6@jasondavis - yes, someone could sniff that particular request, it is true. But because the signature is going to be different for *every single request*, sniffing them does no good. At best, all they can do is send the exact same request! They can't generate a malicious request, because to do that, you need the secret API key. And if they try to muck with the data in the request they sniffed, say changing the email to `evil@example.com`, the signature will no longer match, and the REST server will reject the request when it attempts to validate it. – zombat Dec 22 '11 at 17:18
-
4Isn't this method insecure if the user is making the request from javascript and thus exposing their API key to the whole world? Someone could view source, get the API key and use the same methods to generate a valid request to make call to the server. Or am I missing something. – Barış Uşaklı Oct 17 '12 at 22:00
-
@zombat (I don't think so, but anyway..) Is there a method to do that when user fills html form? when I say 'user' I mean a registered user, not a developer who sends his request via his locally PHP file where the secret api key is stored. If there isn't, what should I do? – Luis Jan 20 '13 at 06:28
-
2@zombat that is a very good question baris-usakli has asked. How to hide the API key in a javascript web app? For the phone app market that is using the same API, even there how to create a private API for each downloaded phone app and let the API know about it? – Houman May 18 '13 at 18:44
-
@zombat as far as I can think of the only way how to get over this, is creating an API key based on the username and password. when an account is created, the application creates the key based upon the username and password, and then sends the username & password to the web service. the web service then creates the same key based on the username and password.. But this also seems not very secure to me, so does anybody know a better way how to do this properly in javascript app or a mobile phone app? – Joris416 Dec 08 '13 at 17:08
-
what is the signature that the client generates? How does he generate that? Do the client need the api key to generate the signature? How does he know that if the key is never sent as part of the communication? – ACs Mar 20 '14 at 12:13
-
@zombat you have forgot to explain how signature is generated and how to get it? – Tomas Jul 29 '14 at 19:39
-
what about putting signature in header requests and use TLS for more security – Marcel Djaman May 09 '15 at 13:09
-
There is an important part of the implementation here which I think needs some highlighting. Let's say your app consists of users and articles. If you want to implement a general method using signatures, someone reading this might take user_id and article_id as their params, because adding the method, or the specific text would be too much coding. Your signature would be $sig = hash_hmac("sha256",$params['articleid'].$params['userid'],$API_KEY); In short, this would open the signature for every method done on article [articleid] by user [userid]. So, include the method and _input_ in hmac. – Eltyer Jan 02 '18 at 12:31
The idea of REST is that it's stateless—so no sessions or anything. If you want to authenticate, then this is where keys come in, and keys must be passed for every request, and each request authenticates the user (as REST is stateless, did I mention that?).
There are various ways you can pass a key. You could pass it as a parameter (i.e. http://example.com/api/resource/id?key=api_key) or you can pass it as part of the HTTP headers. I've seen APIs that specify you send your username, and an API key as the password portion of the HTTP basic access authorization header.
An example request:
<?php
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_URL => 'http://example.com/api/resource/id',
CURLOPT_USERPWD => 'martinbean:4eefab4111b2a'
));
$response = curl_exec($ch);
Where martinbean
would be my account username on your website, and 4eefab4111b2a
would be my API key.

- 38,379
- 25
- 128
- 201
-
4This method is simple, but inherently insecure. Someone could easily sniff your API Key and start using it for themselves. If you want your API keys to be private, you don't want to be sending them as part of the request. – zombat Dec 19 '11 at 21:41
-
1Works for Campaign Monitor: http://www.campaignmonitor.com/api/getting-started/#authentication – Martin Bean Dec 20 '11 at 00:17
-
2Campaign Monitor will have a lot of explaining to do the first time one of their customers gets their API key hijacked because they were working on an open wireless connection at Starbucks. – zombat Dec 20 '11 at 23:07
-
Lots of services authenticate in that manner. Campaign Monitor, Freshbooks; even Facebook uses access tokens (although a token can be limited in what it can access). It's a pretty common way to authenticate an API. – Martin Bean Dec 20 '11 at 23:17
-
5Access tokens are a common method, but API Keys are not. Access tokens expire, so it's not as big a security hole if one gets leaked. But if I have your API Key, I can generate as many access tokens as I want. There's a good reason that all these services give you a "secret" key... it's supposed to be a secret. Any service that is having you pass your secret key is doing it wrong. – zombat Dec 21 '11 at 18:11
-
@zombat - If this is in the context of a mobile app or SPA then you're going to have to get the API key to the user *somehow* whenver they log in. How would you recommend going about this? Understandably the less you pass the API key around the better, but at some point you are going to have to trust HTTPS to get the key across safely, just as you trust it when you log into a site with a username/password? – Levi Botelho Dec 28 '14 at 12:03
-
1Note that Campaign Monitor (as of 2017) is using an HTTPS URL to pass their API Key, so it is safe. Not sure if this was true back in 2011 when @zombat noted that it is dangerous to do this. – Scott C Wilson Jul 04 '17 at 15:03