About the Android Makers app, security and google-services.json
Android Makers is over! With over 650 participants and 50 sessions over 2 days in Paris, it was a lot of fun! It was nice to see everyone in person again and discuss all the Android things (and 🍻 too)!
This year, we decided to rewrite the Android app (github) in Jetpack Compose and this too was a lot of fun 😃! We got a functional app in no time and sent it out for public scrutiny. We got a lot of feedback and contributions (did I mention this community rocks? 🤘💙)!
This is the story of how (and why) we made our
google-services.json public so that anyone could easily contribute.
Whenever you setup a Firebase project,
google-services.json comes up pretty quickly in the installation instructions. You usually download it from the Firebase console:
If you open it, you'll find something like this (some values redacted although I'm not 100% sure why, more on that later):
As you can see, there's something called
api_key up there. That sounds pretty secret so certainly it's a good idea to hide it, right? Well... turns out it's not secret at all.
Your Android API key is not secret!
Because your app needs your API key at runtime, the API key must be accessible somewhere in the app. So really it can't be secret. To prove it, let's try to retrieve it.
Start by doing a google search for the Android Makers app apk. This takes you to an ApkPure website that allows you to download the apk:
A click and and a few downloaded MB later, you can open that APK in the Android Studio APK analyzer (Build -> Analyze APK...). Look for a resource that looks like an API key. This
google_api_key string sounds like a good candidate 🙃:
Yup, zoom a little bit... That's the one! So I'm not sure why I redacted it above. Certainly because everything is made to make you believe this should be treated as a secret.
If you ever commit such an API key to Github, all the repository admins will receive an email along these lines:
Well, that's scary 🙀...
Same thing if you publish an app to the Google play that contains your API key in Kotlin code:
This is also scary 😱...
Why all these warnings if the API key can be retrieved in a few minutes by any malicious user? That remains a mystery to me. My current guess is that there's no way to distinguish between a client key and a server key so that warning is sent to anyone indiscriminately. If someone can see of a good reason to show these warnings for an Android API key, please tell me so I can edit this article and redact everything for good 😅 (and invalidate all the things too!).
Developing in the open
After browsing the web a bunch, realising that there was an official answer in the firebase forums (edit: and it's even in the official Firebase documentation as @zsmb13 made me realize later) and double checking that the values were in the apk anyway, we decided to ignore the scary warnings, stop living in fear (🤘) and commit our
What could go wrong?
Assuming someone has your Android API key. What can they do with it?
In our case, the main thing was using our Firestore instance and using our quota. We could imagine someone developing their own service squatting our Firestore instance and therefore not paying the bill at the end of the month 💸.
While technically possible, I'm not sure how practical that would be. Such a service would be dependant on a completely foreign infra and any change would break it almost instantly. I'm not aware any such attack has happened already but maybe it's a matter of time...
Additional (actual) restrictions 🔐
The good news is that the Google Cloud console allows to restrict the API keys 🎉
You can restrict your API key per platform:
In the example above, we restricted the API key to only be callable from Android apps. As @RickClephas points out on Twitter, this works by using two HTTP headers:
X-Android-Cert. So it's not bullet proof but it's still something.
In addition to platforms, you can also restrict your API key to some APIs:
This makes sure no other APIs can be used so it's an additional safety. Make sure to include
Firebase Installations API and
Token Service API or your app will stop working after a few calls.
You can hide your Android API key from Github but this is not going to prevent anyone to get it. If you need to share it with colleagues or your community, it should be pretty safe to commit it despite all the scary warnings.
At the end of the day, security is never a "yes" or "no" kind of question and has more of a continuum of answers. Some might argue that despites all the flaws, hiding your API key from GitHub is still making it harder to retrieve and that's very true. It's also very true that every security measure has a cost, either in terms of raw dollars or in terms of developer experience.
Of all the possibilities:
- Add GCP API key restrictions
- Fine tune your Firestore security rules
- Check your app integrity with something like Firebase App Check
- Obfuscate your client code with something like DexGuard
- Implement server side rate limiting and fraud detection
- Hide your API key from Github
I'd argue that hiding your API key from Github is the one with the worst cost/security ratio. If you're relying on that for your security, you'd better add API key restrictions and double check your Firestore rules instead.
Note: none of the above applies to server API keys of course. If your API keys doesn't end up in a client, make sure to keep them secure in the depths of your infra.
This article was edited on May 10th to add a note about the Firebase official doc, the
X-Android-Cert headers and rework the conclusion to display more possible solutions.
Cover image background by Claire Tresse