Firebase Token Verification - php

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.

Related

Implementing an API key to restrict access to webservice URL

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.

How to do API authentication with curl

I'm new to creating API's and I am making an API for my php site. Now in any case what I am currently doing is having my script do a cURL call to some php file which does all the processing. Aka im doing a POST call for example to an api file which lets say creates a forum post for that user. Now the important thing to me is how do I authenticate and retrieve which user is sending the data. So how do I know the cURL call came from my server?
What I was going to do is have my server have a secret key that is passed in the api call and verified by the api file. The api file would make sure the key is correct and then take whatever username was passed in for example to make a forum post. My only concern is if this key is ever found out im screwed. I also want to be able to have the site work lets say as an android app so I want to be able to make curl calls lets say (not sure if thats possible) and have some authentication key sent to my server but I never want the user to be able to packet inspect for the secret key.
So my question is how can I securely do curl calls, since when I do a curl call it doesn't read any of the $_SESSION values I have set (unless im missing something). Any help is much appreciated. I was also thinking of authenticating using the username and password each time the only problem is I kind of want to avoid having to verify that the username and password is correct every time an api call is done since thats going to be another query that has to be done. But if that is the recommended way or the industry way then ill do it that way. Just looking for how to handle everything the proper way.
You should look into implementing OAuth then.

Paypal WPS credentials

I have my application running nicely, I use Paypal WPS, I left the default values untouched, I tried with the sandbox and everything works nicely, but now, I want to release it, and I'm not quite sure about the credentials...
Default credentials are
<?php
define("DEFAULT_DEV_CENTRAL", "developer");
define("DEFAULT_ENV", "sandbox");
define("DEFAULT_EMAIL_ADDRESS", "sdk.seller#gmail.com");
define("DEFAULT_IDENTITY_TOKEN", "6vwLEY_ogPGnoQac2a0x4PRsSGrmzJPMkyGbJtpiCSwrkYsNSYxWfPY2ZLO");
define("DEFAULT_EWP_CERT_PATH", "cert/my-pubcert.pem");
define("DEFAULT_EWP_PRIVATE_KEY_PATH", "cert/my-prvkey.pem");
define("DEFAULT_EWP_PRIVATE_KEY_PWD", "password");
define("DEFAULT_CERT_ID", "B62GVU8RWNBFC");
define("PAYPAL_CERT_PATH", "cert/paypal_cert_pem.txt");
define("BUTTON_IMAGE", "https://www.paypal.com/en_US/i/btn/x-click-but23.gif");
define("PAYPAL_IPN_LOG", "paypal-ipn.log");
?>
How can I get real credentials? I don't find paypal help very useful... and I'm not used to using SSL, for what I could understand I need to get a private key, a public key, upload my public key to paypal and get a paypal certificate, but I don't know if that's right and, what to do with the other values.
Having just done this myself, it depends on what you're doing with it.
If you're encrypting your buttons dynamically, you need the EWP variables.
This PayPal help file explains how to get the various keys you need using your server command line.
I also found a tutorial and a certificate builder (linked in the tutorial, but I didn't use, so can't confirm how secure it is...)
The first step is to generate a private key and public certificate for yourself. You can use various online resources to do this for you rather than needing to figure out how to do this from command line on your server.
Once you've generated your key and certificate, you need to put them on your server with your website files and set DEFAULT_EWP_PRIVATE_KEY_PATH (private key) and DEFAULT_EWP_CERT_PATH (your public certificate) to the relevant file paths.
Upload the public certificate to PayPal (instructions in linked tutorials, but basically find the EWP settings and upload a new certificate), and set DEFAULT_CERT_ID to the Certificate ID it gives you for that file. It'll also give you a file you can download (their public certificate) - add that to your server and set PAYPAL_CERT_PATH to the path for that file.
PAYPAL_IPN_LOG isn't necessary unless you're using Instant Payment Notification, but is the location of the file you want to use for logging IPN records (if you want to use the default behaviour of the sample IPN listener (IPNListner.php).
BUTTON_IMAGE is obviously enough the image you want to use for your button.
DEFAULT_DEV_CENTRAL can stay as is. I think DEFAULT_ENV is a bit useless outside of development, as I don't know of an alternative to "sandbox" that works, since the standard URI is www.paypal.com/blah
Change DEFAULT_EMAIL_ADDRESS to your own PayPal account. You can also use your Merchant ID, but that seems a bit buggy on the Sandbox with EWP.
I've yet to find anything I'm using that uses DEFAULT_IDENTITY_TOKEN.

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.

Categories