Essentially I want to restrict raw access to a webservice which my app connects to. Essentially, the app is dragging data from 'http://apis.example.com/webservice'. By simply clicking on the link anyone can view the information as it's open.
I'd like to add an API key which I will code into the app.
My question is simply, how do I set up an API key to prevent people accessing the webservice without appending the key? The data is held in MySQL and a PHP script displays the data.
Thanks for your help!
An API key is just a fancy term for a password between machines. All you have to do is think of a random string, send it along with your request, and have the responding server expect the same string or refuse to provide a response.
For example, if your API key is the ever secure password123, you could have a request like this:
http://apis.example.com/webservice?key=password123
and have your server-side code set up like so:
if (!isset($_GET['key']) || ($_GET['key'] !== 'password123')) {
die('You shall not pass!');
} else {
// do your magic
}
This will only run the meat of your API script for those who possess the key.
Having a fixed sting is better than nothing, but anyone inspecting the data can sniff it and steal your key.
You could implement a challenge response setup by coding a secret key into your app, have the app make a request to the server and in response the server returns a random string.
The app performs a calculation on the string using the secret key as a factor and returns the result to the server.
The server has the secret key so knows the result to expect, if it matches then it serves the request.
You can also use API Gateway , there are many opensource solutions ( Kong , Apigee etc ) available which will sit in front of your API and will provide security and many more things to your API.
Related
I have a PHP backend and an Android client. With the client the users can log into my app using either Google or Facebook, both via Firebase. I get the token from the FirebaseUser and send it to my server. It is straightforward that the first section (the header) contains the algorithm (which is RS256) and the second one (the payload) has all the user related data. There's a third section which is the signature of the first two to enable verification on my backend. The problem is, I don't know how to do that. More specifically with what.
I used JWT.io to check my token and tried to verify it with no luck. Since the algorithm used is RS256, the verification should be done via the public key. But what public key? I tried with my app's keystore, tried it with Google's certs, but it just keeps saying it's invalid. I understand that the header's kid field is the signing key's ID and I should look for it, but I don't know where.
The Firebase docs don't help either. There is a guide about ID token verification, but that's just useless because it's Java / Node.JS and it still doesn't say anything about public keys.
So the question is: where do I get the public keys from?
Okay, so I dug into the source of the Firebase Server SDK and found the location of the public keys:
https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com
Don't really know why they just couldn't put it on their website...
Anyways, I'm not sure, but I guess that these keys change on a daily basis (just like the OAuth2 keys do), so you must check and re-cache them on your server every now and then.
Also, you have to check the following values:
alg == "RS256"
iss: https://securetoken.google.com/<firebaseProjectID>
aud: <firebaseProjectID>
sub is non-empty
Found these at this similar question (just scroll to the bottom of the answer), which was found by searching for that specific googleapis.com URL.
I have an android app and a php server. I send some requests, say for login and other request parameters as required by my app; and the server sends back json which is displayed in the app.
My question is how could I prevent other users from accessing the urls via the browser and getting the results?
Also most of the requests should only execute if the user is logged in. That is taken care of in the app but how in the apis? I can send an auth token to be sent along with further requests, but the same can be get by calling the login api via browser and then sending that login token with other apis. How to handle these situations and prevent access via methods other than the app to get the data?
And also does it matter in case of android apps whether I send the login password encrypted or not from the app? Or should they be encrypted at the server and then stored in the database?
This might be a very naive question, but I couldn't find an existing one here on SO. P.S. I know php apache server is not the best option but the client has an existing php server ready with the apis and he wants to use the same in the app.
With my experience in writing a JAVA REST API here are the few suggestions I can make,
1> My question is how could I prevent other users from accessing the urls via the browser and getting the results
ans: If you want the API to respond to only the requests sent by App, You can sign the request with a unique key which is known only to the app and no body else.
For example: You can generate a string by appending all the parameters you send in a specific order like username+password+param1+param2 and then apply SHA 256 or any of the hashing on that string to generate a unique string and send this string as a Authorization header value :)
At API end because you know the oder of the parameter to join, once you recieve a request regenerate the string using parameter that you have received and apply same hashing on the string and check whether the string you generated matches the request Authorization header value or not and respond to only one with proper Authorization value :)
How it works : Being un-aware of Authorization header whenever browser makes a request this vallue will not be there in request hence your APU wont respond.
On the other hand if somebody deliberately tries to track the request and realizes that Authorization header needs to be sent, still wont be able to get response from your API because he will not know the Algorithm used to generate the value nor the sequence of parameter used.
Use Cases:
All Outh API's makes use of same pattern. Read OAuth specs for clarity (Though they use ot for different purpose, you can use it to benifit your situation:) )
Example of API's : Facebook,Twitter,Google,Linked In and so on :)
2>also does it matter in case of android apps whether I send the login password encrypted or not from the app?
Ans:
It does not matter wheteher you use android apps or web browser to make request :) If you are using POST request all your parameters will be encrypted by default :) So dont worry about it :)
3>should they be encrypted at the server and then stored in the database
Ans:
Saving a password in a plain text at your data base can lead to many consequences :) So its always better to save encrypted passwords at data base :) As far as usernames are concerned its ok to save them in plain text :)
Extra peice of Info : Make sure all your api's makes use of Secure channel :) I mean make sure your API's are https :)
Hope my suggestions helped :)
We have built an android app that POST HTTP Request from Android to my PHP server.In response the webservice sends JSON object to android app to show results.
like one of the service is like
http://mydomain.com/test/weather.php?lat=13.4332&long=80.454
Since I am not expert in android so want to handle the security for webservice from PHP end.
How can I secure my webservice call so that only my app can use my webservice. I dont want if someone decrypt the apk and get the webservice URL and use it after customize the ouput data.
How can I achieve this? Please provide me any good example.
If you are able, you should implement the use of HTTPS in your app and this could solve many security problems.
Create a self-signed server SSL certificate and deploy on your web server with the keytool in the Android SDK for this purpose. Then create a self-signed client and deploy that within your application in a custom keystore included in your application as a resource (keytool will generate this as well). Configure the server to require client-side SSL authentication and to only accept the client certificate you generated.
oReilly : Application Security for the Android Platform
or
If it's only your client and your server, you can (and should) use SSL without purchasing anything. You control the server and the client, so each should only trust one certificate, the one belonging to the other and you don't need CAs for this purpose.PHP can receive data via POST or GET out of your site and even the internet browser. One of the methods used to do this is by curl.
You must verify the information received by POST or GET in your PHP, this language has much ability to solve these "problems"; Take a look at this part of the PHP official documentation.
Suppose you're building a login system: Also you can add in the login page place a hidden element with secret unique code that can happend only once, save this secret code in session, so, the loging script look in session for this code, compare with what was posted to the script, should same to proceed.
And, if you want to get the IP address of your visitors:
function getRealIpAddr()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) //check ip from share internet
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //to check ip is pass from proxy
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}
you want want to look up here: Encrypt data within mobile app and send to web service and Web services: how prevent illegal accesses
Make sure you request in the POST request an extra field to check for sender's id. In this field you could use a hash sequence, like one generated by a md5 algorithm.
So, in your Android App you would generate hash string using an identifier and a general string, like so:
$identifier='Here comes an understandable unique Id for your App user';
$common_sequence='Here comes random sequence, the same to be used server-side';
$hash_sequence=crypt($identifier . $common_sequence);
In your POST you have 2 fields for this:
Hash_sequence
UserId
In your server you can re-generate the hash_sequence as you have common_sequence already, and also the userId. Check if they match.
This solution, however, has some weak points, specially the fact that one could use the hash_sequence several times. You could consider inserting a time factor to the generating sequence, for example, like yyyymmddHHmm so the sequence would change each minute.
Crypt() is a function that generates your hash_sequence in PHP. You would need however a way to generate a similar sequence in your App. In this case, a simple MD5 hash might be enough, and there must be a MD5 generator native in Android Dev language.
Best wishes,
i m building a phpapi and in this api will be giving my clients API key generated for each client.
So basically my client will be sending client id,his personal data(to be stored in DB) & an API key to my api.php page and this API key will be placed in a hidden field on clients form.
Now my doubt here is that this key can be seen by anyone if they view page source...
So how can i prevent key from being not seen so that no other clients can use it for them..
OR
Is there any other way to place API key on form so it cannot be seen and use securely..
Every data you send to your client can be viewed by him and others (Sniffers ;) ).
The only way to make the communication between him and you really secure is to use a HTTPS-Connection (=> SSL-Certificate!). Then it is nearly impossible for others to see the API-Key but the client himself will be able to see it in the source-code. If the client's pc has to know the key then the client will be always able to view it.
Would you be able to use php sessions? This way the api key stays on the server and coupled with https would provide great security.
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.