Your Publicly Accessible Google API Key Could Be Giving Hackers Access to Your Files and Photos!
We’ve all seen them before, those long, seemingly random strings of characters starting with AIza. Yes, that’s right, the ubiquitous Google API key.
Table of contents
Why are Google API keys everywhere?
How do you protect your own Google API keys?
Google is one of the largest organizations in the world and as such, they have an API for literally everything. What this means for security researchers is that Google API keys are also literally everywhere.
If you're a developer, you probably have a few Google API keys of your own. Even if you’re not a developer, chances are you've seen a Google API key somewhere on the Internet, be it in the source code of a website, app, blog post, or forum discussion. These API keys are a necessary evil - they allow us to access Google's vast resources, but they can also be a source for potential abuse.
Our spiderSilk Resonance product scans the open internet on a continuous basis, giving us great insight into who runs what and where. As such, we regularly find Google API keys associated with our customers. Sometimes those Google API keys are scoped well beyond simply displaying information from Google Maps. By the end of this article, I hope to show how simply exposing a Google API Key through your website’s source code can end up costing your organization.
Why are Google API keys everywhere?
There are a few reasons.
First, many developers use public code repositories like GitHub to store and share their code. And when code is stored in a public repository, it is typically accessible to anyone. This means if a developer is using a Google API key and includes it in their code, anyone with access to the code can see the key.
Second, many developers use online forums and other public places like StackOverflow to ask questions and share information about their code. And when code is shared in a public place, again, it is typically accessible to anyone.
Then again, some developers expose their key directly in their website's source code on their production web server so anyone who visits their website can view the key, instead of storing the key in a file outside of their web-accessible directories or in an environment variable, as Google suggests.
Finally, some developers include their API keys in their code when they share it through a private message or maybe through one of those sites like PasteBin. This can happen if they forget to remove the key before sharing their code, or if they mistakenly include their key in a comment or other non-code text.
What can we do about it? How do you protect your own Google API keys?
First, never include API keys in your code. Anyone with access to your code, be it through your public website, a forum post asking how to resolve an issue, or a private message to an associate, will now have access to your API key.
Second, be careful about where you store your API keys. Don’t put them on a sticky note attached to your monitor, a text file on your PC desktop, or in the README.md of your GitHub repository.
Lastly, read Google’s best practices for securing your API keys. It’s an amazing resource and it absolutely cannot help you if you do not read it.
How to test Google API keys?
I’m glad you asked. When I queried our internal tools, we found nearly 300,000 Google API keys, and that’s just from the public Internet on ports 80 and 443 with a baseURL of /. Since I didn’t crawl past /, you’ll have to imagine how many Google API keys are lurking in other places like GitHub, GitLab, BitBucket, PasteBin, StackExchange, or even inside of your favorite mobile app.
If you have gone looking and found your own Google API key floating around the Internet, the first thing you should do is not panic. Seriously? Seriously! You can and should, if you have not already done so, add restrictions to your key so that it can only be used from certain IP addresses or domain names. Google also lets you set billing limits too, but we aren’t going to talk about that since you’re now armed with enough information to continue down that path on your own.
If you found someone else’s Google API key floating around the Internet, chances are, you won’t get a big fat bug bounty. In fact, most bug bounty programs will mark the issue as N/A or maybe Informative if they’re feeling generous. We all know there is malicious intent all across the Internet, however, it’s never enough to prove intent, we need to show some sort of impact, therefore, I wrote a nuclei template for this very task.
id: api-google-drive
info:
name: Google Drive API Test
author: geeknik
severity: info
reference:
- https://developers.google.com/drive/api/guides/about-sdk
tags: token-spray,google,drive
self-contained: true
requests:
- raw:
- |
GET https://www.googleapis.com/drive/v3/files/{{randstr}}.txt/%3fkey={{token}}&supportsAllDrives=true HTTP/1.1
Referer: {{referer}}
Content-Type:application/json
matchers:
- type: word
part: body
words:
- 'File not found: {{randstr}}.txt.'
This is a simple template that checks for the existence of a file that doesn’t exist, very non-invasive, and very easy to use:
Unfortunately due to the way this particular API works, you have to include a referer or the test will simply fail. Feel free to improve upon my template and submit a PR to the community repository.
I was also tasked with creating a CSV file containing the IP address, the Company Name, and the Google API Key so we could have a list of organizations in a central location when it came time to start sending notifications. Considering the amount of data I had to go through, I felt as though Python was a good choice for automating this task.
#!/usr/bin/env python3
import csv
import re
import os
import sys
import subprocess
from ipwhois import IPWhois
from time import sleep
import random
def main():
ip_list = []
with open("input.txt", "r") as f:
for line in f:
line = line.strip()
match = re.search(r'[0-9]+(?:\.[0-9]+){3}', line)
if match:
ip_addr = match.group()
print("IP Address: {}".format(ip_addr))
match = re.search(r'AIza[0-9A-Za-z\-_]{35}', line)
if match:
google_api_key = match.group()
print("Google API Key: {}".format(google_api_key))
ip_list.append({'IP Address':ip_addr, 'Google API Key':google_api_key})
print("\n")
print(ip_list)
print("\n")
with open("output.csv", "w") as f:
writer = csv.writer(f)
writer.writerow(['IP Address', 'Company Name', 'Google API Key'])
for ip in ip_list:
whois = IPWhois(ip['IP Address'])
results = whois.lookup_whois()
company_name = results['nets'][0]['description']
writer.writerow([ip['IP Address'], company_name, ip['Google API Key']])
print("IP Address: {}\nCompany Name: {}\nGoogle API Key: {}\n".format(ip['IP Address'], company_name, ip['Google API Key']))
sleep(random.randint(3,10))
if __name__ == "__main__":
main()
This script takes nuclei output, grabs the IP address and the Google API Key from each line and performs a WHOIS lookup on the IP. Here is an example of the output produced in the console.
And an example of the CSV output.
Of the nearly 300,000 Google API keys I discovered in our data, many were duplicates and many were locked down. However, I was able to break it down by API for you:
API |
# of API Keys |
Cost for 1000 requests |
29 |
$0 |
|
185 |
$5 |
|
9517 |
$5 |
|
8653 |
$5 |
|
0 |
N/A |
|
11544 |
$17 |
|
9008 |
$5 |
|
20 |
$5 |
|
0 |
N/A |
|
18 |
$0 |
|
18 |
$0 |
|
11547 |
? |
|
1614 |
$10 |
|
11550 |
$2.83 |
|
11546 |
$17 |
|
11526 |
$7 |
|
0 |
N/A |
|
1614 |
$5 |
|
0 |
N/A |
|
18 |
? |
|
6726 |
$2 |
|
4765 |
$7 |
|
11545 |
$32 |
|
1805 |
$5 |
Your results may vary due to network conditions, rate limits, or IP reputation. Plus, some of the API keys likely have some sort of an origin or referer restriction in place. We’ve responsibly disclosed these findings to the affected organizations while also extending an invitation for them to peer into their attack surface using our spiderSilk Resonance product.
Now for the part, everyone has been waiting patiently for, “How many Google API keys are valid for Google Drive?” Well, first off, we have to worry about passing a referer to this particular Google API, so we had to write more Python:
import sys
import os
def main():
if not os.path.exists(sys.argv[1]):
print("[-] File does not exist")
exit(0)
f = open(sys.argv[1], "r")
for line in f.readlines():
line = line.split()
bash_script = "nuclei -silent -t token-spray/api-google-drive.yaml -var referer=" + line[3] + " -var token=" + line[4]
print(bash_script)
if __name__ == "__main__":
main()
To run the script:
Our newly created bash script looks properly formatted, so let’s run it!
Now that the heavy lifting is done, we found 1,419 Google API keys on the Internet which have access to Google Drive:
If you’re interested in doing your own research into Google API keys, there are currently, as of this writing, 24 Google API token-spray templates in the public repository.
The security advice in this article is valid for any API key, however, you should keep in mind that most organizations aren’t going to let you place as many restrictions on your API key as Google does, so in the end, you really should take better care of your API keys. Good luck out there!
spiderSilk is re-imagining threat detection from a multi-solution, resource-intensive proposition today, to an automated, intelligent, cost-effective & easy to deploy one. Schedule a complimentary external attack surface analysis today and let us show you what Resonance has to offer with ZERO setup and 100% non-intrusive!
About the Author
Brian is a Senior Security Engineer with spiderSilk, an emerging leader in attack surface management and threat detection. Brian is an expert in his field and before coming to spiderSilk, helped launch OpenDNS Umbrella before they were acquired by Cisco. He has decades of experience in law enforcement, security research, and system administration. He has been awarded many CVE since 2016 for his research into Firefox, OpenSSL, cURL, PHP, Perl, and more!