Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a PHP API: Checking from which server the API Request comes from

I'm creating a PHP API for a website and I'd want to restrict the API access to domains that are registered on our server (in order to prevent abusing of API usage). So, this is my approach right now, and well, it should look pretty good on paper.

  1. The API is setup at api.example.com.
  2. A user that wants to use the API registers with us, adds his domain and gets an API key.
  3. The user of the API will use his API key to encrypt his request data (via mcrypt) and sends it, via cURL to api.example.com.
  4. My server checks from which domain this API request comes from and matches that domain to an API key in the database. If there is an API key, the API decrypts the request via mcrypt with that key and then using the same method encrypts and sends the result.

I'm stuck on step 4. Originally, I planned to use HTTP_REFERER to check it, but since cURL doesn't send one by default and it could be easily faked in the user-side code (CURLOPT_REFERER as far as I remember), I am stuck here.

Is there a method to know from which domain this API request comes from? I see that it can be done with some popular APIs like the reCAPTCHA one. Checking the _SERVER["REMOTE_HOST"] isn't really an option because of shared hosts (they have the same IPs) so this would not be able to prevent abuse (which would originate mostly from shared servers anyway).

Is there such a method to check for it? Thanks!

like image 865
Jimmie Lin Avatar asked Aug 10 '11 08:08

Jimmie Lin


1 Answers

@Shafee has a good idea it just needed some tweaking. We're focusing on the visible part of the API call, which is the API key. This is visible in the URL and tells the API who is requesting the data. Rather than trying to prevent others from stealing this key and running their own cURL call with the domain they intercepted it from, we can 'just add' another key to the mix, this one not visible to those interceptors. I'm not saying stop checking where the request is coming from, it's still a good way to kick out invalid requests early on in the script, but with a second key, you guarantee that only the person requesting the data actually knows how to get the data (you're trusting them not to give it away to anyone).

So, when the user registers for a key, you're actually assigning two different keys to the user.
API_KEY - The public key that connects you to your domain. The system looks up the domain and key provided in order to find the next key.
MCRYPT_KEY - This is the key that will be used to actually encrypt that data via Mcrypt. Since it's encrypted data, only the requester and the server will know what it is. You use the key to encrypt the data and send the encrypted input with your API key to the server, which finds the key that it needs to decrypt that input via the API key and domain (and IP) that have been provided. If they did not encrypt the data with the proper key, then decrypting with the correct key will return gibberish and the json_decode() call will return NULL, allowing the script to simply return an 'invalid_input' response.

Ultimately with this method, do we even need to check where (domain/IP) the request is coming from? Using this method it really comes down to the API users not giving away their API/MCRYPT key pair to other users, similar to not giving away your username/password. Even so, any website can easily just go sign up to get their own key pair and use the API. Also to note, the API will not even return anything useful to their server unless the user on their end logs in using the correct username and password, so their end will already have that information. The only thing new our server is really returning is their email address upon successful validation of the user. Having said that, do we even need to use cURL? Could we not simply use file_get_contents('http://api.example.com/{$API_KEY}/{$MCRYPT_DATA}')? I realize I'm asking more questions in my answer...

like image 127
animuson Avatar answered Oct 17 '22 10:10

animuson