How do you verify an API request came from a certain domain? - php

I have a web API that I want to allow any domain to submit data to. However, to keep bogus spam down I want to find some way to insure that a request stating it's from a certain domain actually is from that domain and that someone isn't trying to trick me by posting on another domains behalf.
For example, if http://example.com submits some data - thats good. If script kiddie #237 submits data claiming to be example.com - that's bad.
At first I was going to use a secret key system to HMAC sign each request - but signup is going to be open, free, and automated for this API. I'm not sure how I could tell if PersonA or PersonB really owns http://example.com and deserves the API key.

Provide a key file that they will have to upload on that domain. And you check the existence and valid data against your internal database.

Related

php / apache, is there any way to block cross domain post

I have some ajax page with php post (so that CAPTCHA is not a good idea).
some fsockopen or curl could set POST value to steel data with cross domain.
So php / apache, is there any way to block cross domain post?
You are publishing the data. You can't stop people requesting it.
If you want to keep it secret, require authorisation before allowing access.
There are various barriers you can put in people's way—while still keeping the data public—but none of them are difficult to bypass. Testing the user agent doesn't stop the requestor specifying a user-agent that matches a common browser. Requiring a cookie from another page on your site doesn't stop them requesting that page and getting a cookie for their tool. Etc.
A lot web services that offer an API, but don't want just any old person to access the API, use public / private keys via CURL. You would have to do a little research on public / private key encryption if you are not familiar. I am sure there are PHP libraries that get all if not most of this done for you.
Credit card merchants like Authorize.net, and several customer database services that handle PII (Personally Identifiable Information) that I've worked with over the years allow you to post to a URL and retrieve the result. They protect their services using public / private key encryption. They issue me a key and I have to post that key along with my request data. The key is usually sent in a header, hence the need to use CURL.
Background Information:
http://computer.howstuffworks.com/encryption3.htm
PHP Examples:
http://www.joeldare.com/wiki/php:php_public_private_key_cryptography
Note: the benefit of this system is that you have absolute control over who is allowed to post to the PHP script on your server. If you don't need that level of control, you can just use something called "http basic auth". Place your sensitive scripts in a directory and protect it with .htaccess authentication. Here is a tool to get you started. http://www.htaccesstools.com/htaccess-authentication/
When you make requests via ajax (jquery, I take it) you can pass the login and password. Just make sure you use an SSL connection in your ajax (HTTPS). Otherwise, people can sniff out your login and password as it will be sent in plain text without the use of SSL.
If you do use basic auth instead of public/private keys, your domain will need an SSL cert if it does not already have one. You can buy one from someplace like Thawte.com (not cheap), or self-sign your own certificate. However, if you're going to go through all that trouble, you're 80% to the point of just going the public/private key encryption route. Self-signed certificates usually prompt the user's browser with a warning. This scares away a lot of people.

api security implementation php

I have a site that allow users to check their statistics (number of file uploaded, how many files they have, quotas, type of access etc...)
I create a section on my site: api.domain.com
This can be access via curl or a web browser since I am returning a json object or xml (depending on the user prefs)
My question is this: should I restrict the access using a user/pass or should I create a hash of the user file?
for example:
scenario 1:
The user create a php curl that sends user and pass via post or the curl auth and get back the results, parse it etc...
For me this is secure but the user has to maintain his script if they change their pass
scenario 2
The user access a file like: api.domain.com/j355HGssgf3HESAjh45jusf4325GSj5hbsHhdh5HGHFS3732he4548475wbe3447nSNe5XfgjhGJ and then access the data
This one, nothing to maintain
Here's my thoughts on this.
If you return a page not found or an error message (with a good 200 OK status) when a request is made to your page, it might send to bots or UN-trustable people a clue on what can they get and try again and again and again ...
On the other hand, if you send a access denied, like a 401, or better a 500 error code to simulate a server error, these bots or UN-trustable people might go away for good.
I will say the user/password method is a little more secure in this case.
Now, would you trust simply entering a big string (like scenario 2) when you check your mail? or your bank account information?
If it was me I would give each account the ability to create a unique api key,
api.domain.com/?key=<API.KEY>
for more security measure you could make it only accept connection via the post method with the api key. If contacted via get then show an error etc.
Then if they change their pass/username it won't affect their api key

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.
The API is setup at api.example.com.
A user that wants to use the API registers with us, adds his domain and gets an API key.
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.
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!
#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...
You can varify what ip the request comes from, and you ofen can do a ptr search to get a domain name for that ip, but probely the ip adress have more then one domain, and you end up whit the wrong one, so i recomendate that the client send his domainname in the reques, maybe whit HTTP_REFERER, and that you make a dns check if that domain points to the ip asking for it, but notice that a domain, like google.com, can point to more then one ip.
(the ip could probely be faked to, whit some good hacking skill, but thats out of my knowledge)
How about introducing a second variable like lets say an app id. When a user registers her domain, associate this id with the domain. The user needs to submit the app id with each request without encryption along with the encrypted api call. Then you can look up the app id get the app secret and try to decrypt?
In order to best prevent abuse of your API, limit either the speed of requests, or limit the number of requests they can make. If someone is stupid and shares their API key, they'll only be limiting their own API usage, making it more economical for people who intend on abusing the API to get their own key.
Plus, what if someone decides to implement a desktop application using your API? Surely they won't require their users to send their IP addresses to them so that they can whitelist them?
Also, you can combine limiting speed/limiting requests, and limit speed based on the number of requests like how Verizon limits the speed of their 3G network if you pass a certain amount of data usage.

Distributing and using API-keys for web-applications

I have a web-application for which I'm building a Drupal module that allows my customers to access certain data on my application.
I intend to distribute secret API-keys to my customers who need to enter that value in their copy of the Drupal module. This Drupal module then talks to my web-application, but I need to make sure that the POST requests are indeed coming from that source.
How can this 'secret key' be used to pass some information that when my application receives it, it knows:
(a) its from that client's server.
(b) it hasnt been eavesdropped on / copied and used by someone else?
Should I be using this API-key as a password to encrypt some data that matches the rest of the POST request? When receiving it, I decrypt it using my copy of their API-key and it if matches the rest of the data, I consider it validated?
Is there a frame-work that does this for me? Something within Zend?
Use HTTPS and just send the API key in the request. It's that simple.
If you use HTTP then you are going to reinvent the wheel.
Update:
Here is an update after reading the comments, because in the question you didn't explain that you want to give the API keys to visitors of the website (in which case you would be screwed no matter what you do).
The comment by juanpaco explains what to do (and what I originally assumed that you're doing anyway) but I'll try to explained it in a little bit more detail.
The most important thing is that you don't use the API key in the web form. The API key is only used in the communication between your customers servers and your API server.
Here is a simplified explanation:
You give your customer a key and some software/module/library to install on his server.
When a visitor visits your customer's website he sees some HTML generated by your module that does not include any API key and can communicate only with your customer's server (with HTTPS if there is any sensitive information or user accounts involved at all).
Your module on the customer's server gets the request from the visitor.
Your module connects to your server using the API key (with HTTPS).
Your API server responds to the customer's server.
The customer's server responds to the visitor.
Your API key is never sent in the cleartext and never given to website visitor.
This is the only reasonable way to use API keys and after I first read your questions I assumed that you are concerned about the safety of sending your API keys between your servers and the servers of your customers.
If your customers were to give their keys to every visitor of their websites then those visitors would always be able to know them, no matter how hard you would try to make it. Giving visitors API keys and making them possible to use but impossible to read would be impossible. Not hard - impossible. No matter what protocols, encryption or anything you use.
(Thanks to juanpaco for bringing this old answer to my attention.)
Collect and store every client incoming url(e.g. www.authorisedclienturl.com) as part of the parameters you would store on your server before generating an API key to be shared with the client.
The client will use HTTPS to send the API key in the request boby from their registred authorised client urls only.
Use the API key to decript the client information on your server and retrieve the registered client url, verify that the incoming request url is present in the registered urls, then accept and proceed with other processes.

How to validate a user through an AJAX request?

We have a webpage that we provide to partner companies via an iFrame. The iFrame contains several javascript files that make ajax requests to our server for data. The iFrame itself requires an API Key that is keyed to the domain of the partner. This prevents the iFrame from displaying if it is installed on a domain that isn't registered. However, it would be pretty easy to simply copy the contents and javascript files of the iFrame from a registered site and host them on a non-registered site.
Ideally we'd like to use the API key to restrict Ajax requests and prevent our server from providing the requested data for non-registered sites. However, it appears that the HTTP_REFERER server variable is not set for Ajax requests. How can we tell what site that the request is coming from? Is it possible? If not, how can we prevent unauthorized access?
Relying on HTTP_REFERER isn't the way to go. You want your client's website to use an API to contact your website over a secure link, and get a temporary session string, which is then used as part of the source url for the IFRAME, which is how google does it (not with referer.)
Make the url for the IFRAME valid for a limited time, after which you display a nice message about going back to the client's page to start over.
When the iframe is requested you can generate a unique ID on your server, then set that as a cookie on the client. Every AJAX request should contain that cookie. Only keep around the ID's for the last hour or so.
You can never rely on HTTP_REFERER because some proxy servers and firewalls will strip it out to preserve users' privacy.
The challenge is that the iframe is authorized to a specific domain, so my API Key is tied to that. I followed the following tutorial to generate my API keys.
https://ajax.dev.java.net/ajax/api-keys
Do you think that relying on the HTTP_REFERER variable will prevent users from accessing the iframe? Sun claims that Google uses this method for Google Maps API authentication.
Once the API key has been authenticated, then the cookie approach should do the trick, I think. Thanks!

Categories