What is a Secret Key?

Secret keys are commonly used amongst developers when they have to deal with APIs. A secret key, also known as a “private key”, is a unique key that is provided for you to be able to make requests from an API.  It is like a credential for the API to verify that you are an authorized user.  Once authorized, you are able to make any requests from the API that you’re using.

Why Should We Hide Secret Keys?

It is important to keep your secret keys private.  An exposed secret key can cause many unfortunate events.  For example, if your secret key is exposed, strangers are able to make any API call they wish.  This includes having the ability to leak sensitive information, overload your database with post requests, and delete something from your database.

Check out this how-t0-video to learn the importance of web page security and follow along with the steps below.

Best Practices for Key Management

Developers have their own ways of hiding their secret keys.  Here are a few ways that you can do to protect your key:

  1. Have a middleware API to make calls to the backend.  The middleware is a controller that sits between the frontend and the backend. This allows you to have your API key sit in the backend without needing to expose the API key on the frontend.
  2. Adding another route in app.js to host your frontend.
  3. Placing your keys in a .env file.
  4. Creating a webhook to automatically retrieve data.

Poor Practices for Key Management

There are also poor practices that developers may accidentally do such as:

  1. Hardcoding the API key into your frontend code.
  2. Module and Classic scripts make no difference in hiding your secret keys. Module scripts enable export and import functionality which can remove secret keys from your code but the key still can be viewed in the Network tab in the developer's console.
  3. Not setting a restriction on your API keys so only certain IP addresses can access it.

Demo of Poor Key Management

Now let’s build a simple application that can detect secret keys in your code and how you can hide it.

Getting Started

For this project, we will use:

First, let's make a directory called secret_key_demo and move into that directory in the terminal.  You need to install NodeJS, once you’ve installed it you need to run

npm init
terminal

in your terminal to make a package.json file.  Then run

npm install axios dotenv
terminal

to install axios and dotenv to your application.  You can check if Axios is installed if you see their names under dependencies:

secret_key_demo/package.json

Creating a File to Make our API Calls

Now, let's create a file named App.js.  We will use HacWare’s API documentation as an example to validate the secret key.  I've commented on variables that we would have to replace later with a “!”:

const axios = require('axios');
require('dotenv').config();

var domain = '{domain}' //! company sub domain

var path = '/api/v1/auth/';
var base_url = 'https://'+domain;
var route = base_url + path;

var uid = '{uid}'; //! app id
var sec = '{sec}'; //! secret key

axios.post(
    route,
    {
        "uid": uid,
        "sec": sec
    },
).then(res => {
    console.log(`statusCode: ${res.status}`)
    console.log(res.data)
}).catch(error => {
    console.error(error)
})
secret_key_demo/app.js

Different APIs require different variables to authenticate a user.  For HacWare’s API, we will need a domain, App ID and a secret key for HacWare’s API to work.  This is where we will get them from:

Checking your Frontend Code for Secret Keys

A bad practice I've mentioned earlier was hardcoding the API key into your frontend code. If you have accidentally hardcoded a secret key to your code, there is a way to scan your code locally by using gitleaks.  Lets look at this code with fake secret keys hardcoded:

const axios = require('axios');
require('dotenv').config();

var domain = 'hacwareinc.hacware.com' //! company sub domain

var path = '/api/v1/auth/';
var base_url = 'https://'+domain;
var route = base_url + path;

var uid = '123123123ABCDEFGHEXAMPLE'; //! app id
var sec = '123123123ABCDEFGHEXAMPLE'; //! secret key

axios.post(
    route,
    {
        "uid": uid,
        "sec": sec
    },
).then(res => {
    console.log(`statusCode: ${res.status}`)
    console.log(res.data)
}).catch(error => {
    console.error(error)
})
secret_key_demo/app.js

Detecting Secret Keys using Gitleaks

Gitleaks only detects well known keys such as AWS access key, Facebook secret keys, Twitter Client ID, and more.  So, we have to make our own custom rules to detect secret keys.  

For MacOs user, the install process is easy with Homebrew:

brew install gitleaks
gitleaks --config-path=gitleaks.toml --path=. -v --no-git

For windows, download gitleaks-windows.amd64.exe from the gitleaks Github and place that file into the secret_keys_demo folder.

Create a gitleaks.toml file and paste this into the file:

[[rules]]
  description = "secret leaked"
  regex = '''[A-Z0-3]{24}'''
  file = '''app.js'''
secret_key_demo/gitleaks.toml

To translate this file to English, it is saying that if there is a string with 24 characters that has the letters from A-Z and numbers from 0-3 in app.js, then it is a secret key.

Then I ran this command into the terminal:

gitleaks-windows-amd64.exe gitleaks --config-path=gitleaks.toml -v --no-git
Note: This command may be different in MacOS and Linux. Please refer to gitleaks for more information.

Gitleaks will then scan the whole directory.  Here are the results I get:

Gitleaks has found 2 leaks, and it detected the secret keys containing "123123123ABCDEFGHEXAMPLE" in app.js.  This shows that the scan has worked.

Now, how can we get rid of these leaks?

Demo of Proper Key Management

We can make a new file called .env where we will add our secret keys as environment variables. Here’s an article to learn more about environment variables.

Here is what should be done inside the .env file.  We put the keys into their respective variables:

COMPANY_SUB_DOMAIN="hacwareinc.hacware.com"
APP_ID="123123123ABCDEFGHEXAMPLE"
APP_SECRET="123123123ABCDEFGHEXAMPLE"
sample .env

Committing into a Repository

If you want to commit your application into a repository, you must make sure you do not commit your .env file.

You can do this by adding a .gitignore file, and in that file you should put:

.env
gitleaks.toml
gitleaks-windows-amd64.exe
server_key_demo/.gitignore

So when you want to commit something into your repository, git will not track it (along with gitleaks files) and you won’t accidentally commit it.  As seen on the picture, git is not showing .env under untracked files because it is ignored:

Alternate Code Scanning tools

It is never a good idea to upload your secret keys anywhere, especially in version control such as Github and Bitbucket.  We know that we can scan our code locally for leaked keys using Gitleaks, but there are more applications that can scan your repositories for vulnerabilities.  These applications include:

Where do I Store Keys?

Now, let’s look at this part of App.js:

const axios = require('axios');
require('dotenv').config();

var domain = 'hacwareinc.hacware.com' //! company sub domain

var path = '/api/v1/auth/';
var base_url = 'https://'+domain;
var route = base_url + path;

var uid = '123123123ABCDEFGHEXAMPLE'; //! app id
var sec = '123123123ABCDEFGHEXAMPLE'; //! secret key
server_key_demo/app.js

We can set the variables that I’ve commented with their respective environment variables using process.env.<ENVIRONMENT VARIABLE NAME>.  Thanks to the dotenv package we’ve installed, NodeJS to process the .env file as an environment variable.

var domain = process.env.COMPANY_SUB_DOMAIN //! company sub domain

var path = '/api/v1/auth/';
var base_url = 'https://'+domain;
var route = base_url + path;

var uid = process.env.APP_ID; //! app id
var sec = process.env.APP_SECRET; //! secret key
server_key_demo/app.js

Let's scan our code again with Gitleaks

We can also see that there are now no leaks shown in our app.js file.

Now, let’s run

node app.js

with real HacWare API keys to make sure it is working properly.

I have a 202 status code along with data showing, so that means it worked!

Conclusion

There are many ways to protect your secret keys from being exposed.  As long as your keys aren’t uploaded into a public repository, that increases your chance that your application will not be compromised.  Having extra layers to hide your API key is also always a good thing to do, not just keeping your secrets in a .env file.

References:

  1. https://gomakethings.com/keeping-api-credentials-secret-with-vanilla-javascript/
  2. http://billpatrianakos.me/blog/2016/02/15/securing-api-keys-in-a-javascript-single-page-app/
  3. https://blog.logrocket.com/best-practices-for-managing-and-storing-secrets-in-frontend-development/
  4. https://www.freecodecamp.org/news/private-api-keys/

HacWare makes it super easy for Software Developers and IT teams to launch hyper custom cybersecurity education solutions to combat phishing attacks. Learn more about HacWare at hacware.com.

Also, check out our free secure code streams on Twitch!