FCrDNS (Forward-Confirmed Reverse DNS) on Firebase

TL;DR

Around January 2018 I had been trying hard to make FCrDNS on Firebase work… However, after countless hours of hopeless development, I felt like something was wrong, so I asked for help on StackoverflowDoug Stevenson and Thomas Bouldin, two developers from the Google Development Team came to my rescue and kudos for their quick response! At that time they confirmed that reverse DNS lookup was not possible in Firebase Functions, so all I could do was submitting a feature request.

fcrdns firebase wanari

 

On the 15th February 2018, Doug added the following: 

It looks like the change was made to allow this, but it’s not clear to me if it’s already available, or when it will be made available.

As of 11th May this feature has been published, and it’s working! Yeey!

fcrdns how to

However, there were still some difficulties, which I am going to talk about later on, as well as the -actually pretty simple- implementation of the solution.

Why don’t you just use an existing implementation to do this? Why are you even writing this article?

The answer to this question is quite simple:

  1. The existing implementations are using callbacks. But we are in 2018 and playing around at the Callback hell is not sexy anymore.
  2. Some other packages use more modern Node.js features, which are not supported by Node v6.11.5, which is the newest version, supported by the latest Firebase-functions library. (This also means the async await blocks are not available (sad))
  3. It seemed like a fun thing to implement ^_^

So, what is FCrDNS and why would you use it?

A Forward-Confirmed Reverse DNS check is a common anti-spam security feature on mailservers. However, it could also be used in other cases, for instance when you have a webhook, which receives POST requests frequently from another server.

In our case, we were developing a service, which is partially built on Fitbit’s Web API. Our backend is subscribed to our user’s live Fitbit data via the Fitbit’s Subscription API. After successfully registering our backend in Fitbit’s servers, we start receiving POST requests from the Fitbit API whenever there is an update for a user, who gave us authorisation to read their data. Naturally, we don’t want to allow anyone, other than Fitbit to post data to our servers, otherwise our users’ data could be altered, and the services provided by us would be biased.

Fitbit suggests two types of security measurements. One is the validation of X-Fitbit-Signature header, and the other technique is Forward-Confirmed Reverse DNS check.

For performing FCrDNS, the algorithm is the following in this case:

  1. Our backend receives a POST request on the webhook’s endpoint from IP 169.45.142.104.
  2. We do a reverse IP lookup to retrieve the hostname registered for 169.45.145.104.
  3. We verify that the hostnames resolved are subdomains of fitbit.com (in this case, we’d get [‘api-169-45-141-216.fitbit.com‘] as the result).
  4. Then, we resolve the given hostname and verify if the results include the IP address of the original request.

This, together with the X-Fitbit-Signature validation ensures secure communication between Fitbit and our servers.

Come oon, where’s the source code?

Worry no more, you can find the source code on TeamWanari’s Github account: https://github.com/TeamWanari/fcrDns

But since it’s not too long, I’ll also just paste its current version here (note, that the source code might change as time goes by, so check the link to stay up-to-date).

Usage:


Note, that if your service is behind a proxy, you must configure your Express app as explained in the documentation: http://expressjs.com/en/guide/behind-proxies.html

Otherwise, instead of the expected other service’s IP address, the req.ip is going to return the remote address, which is going to be your proxy server’s address. If you set up your app according to the guide mentioned above req.ip is going to be the upstream address.

How does this work?

The idea is that you call the fcrDns(ipAddress, domainToMatch) function with the ipAddress of the request and the domain that you want this IP address to be confirmed against. Since we needed this feature for a service, that runs as a Firebase function, we could only use modules supported by node v6.11.1, thus for example Node’s Util package was not available at the time, which has the promisify function (added in node v8.0.0) for callback-structured methods. Thus we decided to go with the q library which has a denodeify method to achieve the same in lines 8 and 9.

Then we do a reverse DNS lookup for the IP address in line 11, and then later check if all the hostnames resolved actually end with the domainToPatch parameter. In our case, this is ‘fitbit.com’. Another thing to consider is whether we could actually resolve a hostname. Let’s say we try to run this on a local host and send the request from there as well, so our IP would be 127.0.0.1. Obviously this cannot be resolved into any hostnames, thus in line 12, the hostNames.length === 0 is going to be true. So, if the domains did not end with the expected one or there aren’t any hostnames resolved, we reject the Promise with an error message.

Otherwise, if the hostnames are correct, we try to resolve those hostname(s) into IP address(es) in line 15. If it’s a success, we check if any of the IP addresses resolved equal the original IP address. If the answer is yes, we resolve the promise, otherwise we Reject it with an error, because someone is trying to spoof the IP address.

It is important to note why we check for any of the IP addresses: The reason for this is that there might be multiple A records for a single host lookup. This technique is commonly known as round-robin DNS and it is to introduce load-balancing of services.

Conclusions

I personally had a lot of fun implementing this, though it is unfortunate that Firebase doesn’t actually allow reverse dns lookups at the moment. (sad) Nonetheless, I know that once the possibility to do it on Firebase will be deployed, we are going to be able to use this solution to be more secure. Also, if you want to do this in a non-firebase-based service, you are totally free to do so! (smile) Please leave me a comment if you found a flaw in the logic or you thing something could be improved. One possible improvement would be caching the results. (smile) It could be another nice practice and you are invited to fork the repository if you think you are faster than us. ;P

Wanari is an 18-year-old custom software development company. We shall be back with more posts. If you’d like to see them, follow us on Facebook or LinkedIn – to learn more about us, check out our website.

Zoltan Tudlik

Zoltan Tudlik

#TeamWanari #ESN #eitDigital