Finding and Validating Hardcoded Keys and Secrets


Hardcoded secret keys are a convenient target to Bug Bounty hunters and Attackers. They are easy to spot and they can open a wide gate to sensitive data and privileged access.

Hardcoded secrets caused several high profile breaches in the past, notable ones are:

  • MyCar: MyCar is a vehicle telematics systems. The maker left hardcoded credentials inside its Android and iOS mobile apps. This left tens of thousands of cars vulnerable to hackers, who could locate car, identify them, unlock them, start the car or trigger the alarm.

  • Uber: An Uber employee published plaintext credentials within source code that was then posted on Github. An attacker found the embedded credentials on GitHub, then used them to gain privileged access on Uber’s Amazon AWS Instances. The attacker then demanded a 100k$ ransom, that Uber did pay. The Uber breach resulted in the exposure of information of 57 million customers, plus roughly 600,000 drivers. Once the story became public, Uber paid a settlement of 148M$ and had to delay its IPO.

  • Uniguest: Uniguest provides kiosks (PC, iMac, tablet) available in hotel lobbies (and other places). User can use them to run simple tasks, such as browsing the web or printing boarding passes. The API credentials were hardcoded within the application and were used to dump all the data in the Uniguest cloud database. The data included admin credentials, router and BIOS passwords, product keys and various other sensitive information.

Hardcoded secrets are also commonly reported by Bug Bounty hunters. If you check any of the different Bug Bounty programs, you will find many disclosed reports pointing to the hardcoded keys in the AndroidManifest.xml, Info.plist or some resource file.

Depending on the permissions and use of the key, these vulnerabilities might be awarded up to 1k$. The severity ranges from elevating privileges, accessing sensitive information, over-bill/theft of a service or conducting a denial of service attack.

How to find and validate secrets?

Secrets can be found either statically or dynamically. A common static approach is to search known patterns, for instance searching for strings matching the following regular expression AIza[0-9A-Za-z\\-_]{35}:

$ app8 egrep -r 'AIza[0-9A-Za-z\\-_]{35}' . 
Binary file ./resources.arsc correspondant
Binary file ./app.apk correspondant
Binary file ./classes.dex correspondant                                
./assets/google-services-desktop.json:          "current_key": "AIzaSy....................."

You can find in this repo l4yton/RegHex a list of regular expressions to use.

Because not all API keys and secrets are bad or dangerous, and not all pattern outputs are correct; the keys must be checked and the permissions, roles, scopes, and restrictions (more on that latter) must be enumerated and verified. streaak published a repo streaak/keyhacks listing curl commands to check a wide range of keys.

Below are some examples for Firebase keys and Facebook secrets:

$ curl -s -X POST --header "Authorization: key=AIzaS........." --header "Content-Type:application/json" 'https://fcm.googleapis.com/fcm/send' -d '{"registration_ids":["1"]}'
<HTML>
<HEAD>
<TITLE>INVALID_KEY_TYPE</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>INVALID_KEY_TYPE</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
$ curl https://graph.facebook.com/oauth/access_token\?client_id\=51XXXX\&client_secret\=0cbd4XXXXX\&redirect_uri\=\&grant_type\=client_credentials
{"access_token":"5181XXXXXXXXXXXXXX","token_type":"bearer"}% 

Once a key is found, the API documentation should be your best-friend to determine what permissions does it have and what sort of actions can be performed. Take for instance instance a facebook application, you can then use the https://graph.facebook.com/v8.0/{applicationId}/permissions endpoint to list permissions:

$ curl "https://graph.facebook.com/v8.0/51XXXXXXXX/permissions?access_token=51XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
{"data":[{"permission":"email","status":"live"},{"permission":"pages_show_list","status":"live"},{"permission":"pages_messaging","status":"live"},{"permission":"groups_show_list","status":"live"},{"permission":"pages_read_engagement","status":"live"},{"permission":"public_profile","status":"live"}]}%   

Another CLI friendly tool is Yelp's detect-secret: Yelp/detect-secrets. The tool detects a smaller subset of keys and implements validation for some a well.

Ostorlab automates the process of finding and checking keys and secrets and covers 53 secret types at time this article is written. A secret agent collects keys matching patterns or used dynamically by specific APIs or intercepted over the wire. The agent then checks these keys to confirm if valid by iterating over all known services.

This a screenshot showing an example confirming a valid key and the matching service**.

Hardcoded secrets

Just out of the last 10k apps scanned, we have reported over 200 valid secrets granting access to highly sensitive data or critical services, like Payment or Source Code. Below are some statistics of the number of keys found per service:

Service Name Count
Firebase 35/1000
Google Cloud Platform 31/1000
Twitter 22/1000
Facebook 21/1000
Instagram 18/1000
AWS 18/1000
GitHub 14/1000
PayPal 5/1000
slack 1/1000

How to fix it?

  • Embedding is Acceptable, nothing to do here: Most services provide best practices for how to use the API or secret. Some APIs are acceptable if embedded in an application. Firebase is an example:
Unlike how API keys are typically used, API keys for Firebase services are not used to control access to backend resources; 
that can only be done with Firebase Security Rules. 
Usually, you need to fastidiously guard API keys (for example, by using a vault service or setting the keys as environment variables); 
however, API keys for Firebase services are ok to include in code or checked-in config files.
  • Alternatives are encouraged, I should switch: Some services offer more secure alternatives to embedding credentials (such as Amazon and Google, example from AWS recommendation):
You have a mobile app. Do not embed access keys with the app, even in encrypted storage. 
Instead, use Amazon Cognito to manage user identities in your app. This service lets you authenticate users using Login with Amazon, Facebook, Google, or any OpenID Connect (OIDC)–compatible identity provider. 
You can then use the Amazon Cognito credentials provider to manage credentials that your app uses to make requests to AWS. For more information, see Using the Amazon Cognito Credentials Provider on the AWS Mobile Blog. 
  • Embedding is Dangerous, delegate the actions to the Server: For some services, embedding keys is an invitation to get hacked, see for instance Stripe documentation. In these cases, having the server must perform the interaction with the service.
Your secret API key can be used to make any API call on behalf of your account, such as creating charges or performing refunds. 
Treat your secret API key as you would any other password. Grant access only to those who need it. 
Ensure it is kept out of any version control system you may be using. 
Control access to your key using a password manager or secrets management service.

In live mode, new secret keys are only visible the first time you access them. 
After that, the Dashboard redacts the API key. When the key is revealed, you can leave a note on the Dashboard describing the location on your own systems where you’ve copied it. 
If you lose your secret key, you can’t recover it from the Dashboard and must roll the key or create another one.
  • Hardening through Remote Key fetching and Key Pinning: For services that don't provide a per-user authentication service, and need to be embedded in the application, it’s recommended to retrieve the key from the server. It is also best practice to restrict the key's permissions (principle of least privilege). Some service provide the possibility to PIN keys to an application or domain name. Because this protection is not perfect, rotating the keys is recommended to limit exposure.

API Restrictions