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
1. Why should IÂ care?
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 (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âs Beginnerâs Look at Git if you want to git better.
2. dotenv to the rescue
There are dozens of ways to handle the process. If youâre new to programming and are learning Ruby, I recommend the gem â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 .envfile.
3. Walkthroughâââfrom setup to implementation
- Step 1âââinstall the âdotenvâ gem:
Add gem "dotenv"into your Gemfile and donât forget to run bundle installafter doing so.
- Step 2âââcreate yourÂ
.envfile:
While your terminal is in your project directory, touch .envto create the file. If youâre using VSCode like me, the file will appear in root of your project:
.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:
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 .gitignorefile and add .envto the list like so:**
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
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 by Michael Horowitz) 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.
If you want the ability to use dotenv when running rake consoleto debug or test your API, I wrote this bit of code in my Rakefile:
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.Âē
Citations
[1]: Michael Meli, Matthew R. McNiece, and Bradley Reaves. (February 2019). How Bad Can It Git? Characterizing Secret Leakage in Public GitHub Repositories
[2]: Rubydoc. dotenv Documentation. https://www.rubydoc.info/gems/dotenv/2.0.0#sinatra-or-plain-ol-ruby





Top comments (0)