How should you secure your API token?
Tags: beginners, ruby, security, webdev
Published: 2020-08-18 01:34:17

*Welcome to my first blog post! I’d like to kick things off with a discussion on an important topic and propose an easy-to-implement solution to enable us to get started on what matters — our projects — while protecting ourselves on the internet.*

*This post will contextualize API key management on the presumption that you write in Ruby. However, the concepts herein should be applicable to any language.*

---

## Table of Contents

* [Why should I care?](#S1)
* [‘dotenv’ to the rescue](#S2)
* [Implementation/TL;DR](#S3)
* [Citations](#Sources)

---

### 1. Why should I care? <a name="S1"></a>

We’re ready to move from “Hello World!” to our first project, but how do we start? Learning how to program is a daunting task. Take it from me; I started this journey three weeks ago and I thought I had — at least — the basic best practices committed to memory when I jumped into building my first project: [The Commuter](https://github.com/kylejb/nyc-mta-station-arrival-feed) (*interfaces with NYC’S MTA API via CLI*). Unexpectedly, the biggest obstacles that I have faced to date pertain to everything but programming.

If your first foray into this world was anything like mine, coding best practices were easily forgotten in the face of obstacles. In my situation, I was wrestling with tokens and how to pass their value to the server in order to gain access to their data. I resigned to hardcoding the tokens in my code in order to distill the problem.

Did you happen to instinctually bristle when I admitted to hardcoding my token in my application file? Why? Maybe it was because we were taught dynamic code is better than static code? Or, maybe what I did goes against common sense when dealing with private information — you wouldn’t write your username and password to your email address on a homework assignment after all, right? Regardless of your reason, haven’t we all found ourselves in this situation — hardcoding some variable to help solve a complex problem?

Although there may be a better way (do let me know), I would think this is a reasonable approach to take provided that we refactor our code after testing is complete. However, are you making commits on git (e.g., `git add .`, `git commit -m “yay, api works"`)? Perhaps, you are also pushing that repository onto GitHub for your portfolio or to collaborate with others? Are you completely sure that you did not include those secret tokens within one the many commits or pushes to GitHub?

> …hundreds of thousands of API and cryptographic keys leaked at a rate of thousands per day.¹

Researchers at North Carolina State University (NCSU) — Michael Meli, Matthew R. McNiece, Bradley Reaves — discovered that “even if commit histories are rewritten, secrets can still be recovered.” And, over the course of their study, many repositories publicly expose their key — like I almost did. And thanks to search indexing by companies like Google, it would have taken — on average — 20 seconds for my key to be discoverable through a search query that these researchers utilized.¹

> While we cannot say from this analysis alone why cryptographic keys are leaked, it is clear that the poor practice of embedding secrets in code is a major root cause.¹

Because deleting those branches, files, or editing that line of code now would be too late, we need to set up our project folder correctly. And, on a brief tangent, I also recommend you take a look at [Daniel Hahm](https://medium.com/u/9b4073211b0)’s *[Beginner’s Look at Git](https://medium.com/@dhahm01/a-beginners-look-into-git-commands-cbc57d9f34f4)* if you want to git better.

---

### 2. `dotenv` to the rescue <a name="S2"></a>

There are dozens of ways to handle the process. If you’re new to programming and are learning Ruby, I recommend the [gem ‘dotenv’](https://www.rubydoc.info/gems/dotenv); this is easiest to implement and gets the job done.

Other programming languages have similar libraries that go by different names, but these all expect our file structure to incorporate a file (not folder) called `.env` that stores our secret environment variables, which we can then invoke in our code base as a variable. But, before proceeding further, add `.env` to your list in `.gitignore`. This will ensure that git will never add, commit, and/or push your `.env` file, thereby preventing public API exposure as long as you **only** keep the secret values hardcoded to the `.env`file.

---

### 3. Walkthrough — from setup to implementation <a name="S3"></a>

- Step 1 — install the ‘dotenv’ gem:

Add `gem "dotenv"`into your Gemfile and don’t forget to run `bundle install`after doing so.

- Step 2 — create your `.env`file:

While your terminal is in your project directory, `touch .env`to create the file. If you’re using VSCode like me, the file will appear in root of your project:

![https://cdn-images-1.medium.com/max/800/1*y-qBk-qcviW6MI5Gk7MWcg.png](https://cdn-images-1.medium.com/max/800/1*y-qBk-qcviW6MI5Gk7MWcg.png)

`.env` is known as a “global file” to the `dotenv` gem and expects to find in the main folder of your project.

This is the only file where you should be writing your API key values and setting them equal to a CONSTANT (in Ruby, capitalized variables are constants — not global). Here’s an example of what this should look like:

![https://cdn-images-1.medium.com/max/800/1*de_toSN5uua1QnxpihkIUg.png](https://cdn-images-1.medium.com/max/800/1*de_toSN5uua1QnxpihkIUg.png)

Don’t worry… this isn’t my real API token. :)

You can define as many environment variables and values as you wish here. You can access the value of your key like so: `ENV["YOUR_VAR_NAME"]`. For example, if I wanted to use my API key, as depicted in the example above, I would write `ENV["MTA_KEY"]`in another file. **But, none of this will work until we complete step 3**: requiring the gem and making sure the gem loads so that it can access the locally defined environment variable we created here elsewhere in our project folder.

- Step 3— file configurations:

**:: **MOST IMPORTANT** → create or update your `.gitignore`file and add `.env`to the list like so:**

![https://cdn-images-1.medium.com/max/800/1*FJsq2RwTaS_QhvQvQTuP1A.png](https://cdn-images-1.medium.com/max/800/1*FJsq2RwTaS_QhvQvQTuP1A.png)

Note `.env` on line 15; this ensures that `git` commands will never add, commit, or push this file with your secret keys to the public domain.

:: *SECONDARY CONFIGURATIONS* → making `dotenv` accessible within our project directory:

```
require 'dotenv'
Dotenv.load
```

![https://cdn-images-1.medium.com/max/600/1*9nIGnSF18F78AED-vFfItg.png](https://cdn-images-1.medium.com/max/600/1*9nIGnSF18F78AED-vFfItg.png)

In my app, class MtaRunner is the only model that requires access to my MTA API key, so this would suffice.

If you’ve built a custom class ([learn more about classes here](https://medium.com/@mjhorowitz714/object-oriented-programming-in-ruby-9d02e8654659?sk=8300f1ff5da9c14b04adb18d16bfac3f) by [Michael Horowitz](https://medium.com/u/1e6589752708)) for your API calls, you may add the aforementioned lines above your class definition as depicted on the left. Alternatively, you may also add the aforementioned lines of code into your `environment.rb` file provided that your project file is linked up to all your necessary folders/files properly; the benefit of adding the two lines to your `environment.rb` file is you can access your key in all your models without needing to write `require 'dotenv'`on each file.

---

**<BONUS>**

If you want the ability to use dotenv when running `rake console`to debug or test your API, I wrote this bit of code in my `Rakefile`:

![https://cdn-images-1.medium.com/max/800/1*H3rv-FU6N_tFgpXIBAo7ww.png](https://cdn-images-1.medium.com/max/800/1*H3rv-FU6N_tFgpXIBAo7ww.png)

The change was done on line 3 and line 7. For any task(s) that may require `dotenv`, you can follow a similar pattern to ensure it runs concurrently with that desired task.²

**</BONUS>**

---

### Citations <a name="Sources"></a>

[1]: Michael Meli, Matthew R. McNiece, and Bradley Reaves. (February 2019). *[How Bad Can It Git? Characterizing Secret Leakage in Public GitHub Repositories](https://www.ndss-symposium.org/wp-content/uploads/2019/02/ndss2019_04B-3_Meli_paper.pdf)*

[2]: Rubydoc. *[dotenv Documentation.](https://www.rubydoc.info/gems/dotenv/2.0.0#sinatra-or-plain-ol-ruby)* [https://www.rubydoc.info/gems/dotenv/2.0.0#sinatra-or-plain-ol-ruby](https://www.rubydoc.info/gems/dotenv/2.0.0#sinatra-or-plain-ol-ruby)