Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly restrict Google Maps (Places, Geocoding) API calls to Android and iOS Apps (written in Flutter)?

I have a mobile app written in Flutter. I am calling Google API's for Places and for Geocoding. Since the calls to these services are made by including the key in the url it is very important that this keys be restricted so that anyone can't just intercept and use them to make many calls and rack up a big account for us with Google.

Examples of API calls are:

https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$input&types=address&language=$lang&components=country:za&key=$apiKey and https://maps.googleapis.com/maps/api/geocode/json?latlng=$lat,$lon&key=$apiKey

(where $apiKey=our Google Maps API Credentials Key $input, $lang, $lat and $lon represent other variables)

Google currently allows the following Application Restrictions on API Credentials:

  • None
  • HTTP Referrers
  • IP Addresses
  • Android Apps
  • iOS Apps

When I don't have any restrictions on my key (option "None") then my mobile application works perfectly fine. However, if I choose to restrict it to iOS Apps (and specify the Bundle Identifier for my app) I get the error "This IP, site or mobile application is not authorized to use this API key". The same happens when I restrict the key to Android Apps and specify my Package Id and SHA1 signature for the Android app.

What am I missing? According to https://developers.google.com/maps/api-key-best-practices#restrict_apikey I should be fine to restrict the key to my specific mobile application. It seems like Google Maps is not picking up that I'm indeed making the call from the correct mobile app. The error occurs regardless of whether I run this in the Simulator or on an actual device in either debug or release mode. (I have tested the iOS version of the app from Test Flight too. When I remove the restrictions, my api calls work; when I restrict it only to my iOS app's Bundle Identifier it stops working.) Is there anything else I need to configure? Is the problem perhaps that the app is written in Flutter?

I found some links (like this) that suggest that mobile applications should never use the key directly in the url but should rather use our own server as a proxy to Google and then we should restrict access to our server's IP. This seems like an unnecessary overhead since the very existence of the option to restrict the key to specific app id's and platforms suggests that this should be possible (as does the Google documentation I refer to).

like image 567
Stanley Avatar asked Oct 07 '20 15:10

Stanley


People also ask

How do I restrict Google Maps API?

Set an application restriction for an API keyGo to the Credentials page. Select the API key that you want to set a restriction on. The API key property page appears. Under Key restrictions, select Application restrictions.


1 Answers

I ended up creating a proxy server. (More on that below).

Thinking about it critically I realised there would not really be any way for the API to discern whether incoming requests are being made from the specific mobile app or not so I don't think the restriction to specific iOS or Android apps is really possible for https requests.

I also investigated some of the flutter plugins provided but most of them seem to use google_maps_webservice in the background. And google_maps_webservice requires either a key in the app code or a proxy to be specified. Most of the plugins derived from google_maps_webservice don't even offer the proxy option and requires a key to be specified. So even if these plugins are used, one ends up "giving away" your key in the app code which makes it possible to reverse-engineer and obtain your code. (It ends up in the AppDelegate.m file on iOS and AndroidManifest.xml in Android). Google recommends against giving out your api key in this way.

So I ended up creating a proxy server. Requests are made to my proxy without the key and then the proxy passes on the request to Google after adding the secret key. The proxy then returns the response to the app. The app and traffic between app and server never contains a key.

Luckily my app was already using AWS and users were signing in with Cognito. So I could create the Proxy easily in API Gateway and I let the app authenticate to my proxy server using Cognito. This ensures that only users validly logged in on my app in Cognito will be able to call the proxy and keys are always kept secret.

like image 190
Stanley Avatar answered Sep 23 '22 07:09

Stanley