Keeping data secured in Git Repositories with environment variables

Keeping data secured in Git Repositories with environment variables

Starting with web development, we tend to create a ton of small projects. Building small things keeps us learning, so we pile up these publicly in our Github account and invite others to see our hard work. This opens up a lot of opportunities which is great! But, we should be careful to what we upload to our repository, prying eyes (or bots 🤖!) can cause mischief with our API Keys or Secrets.

Configuration & Environments Variables

When we configure services or libraries like Stripe, Firebase, or API calls, we add sensitive information. Whether that data is a token, a username or a picture of your cat 🐈, it should remain a secret and safe.

This configuration usually comes in the form of constant variables which value is assigned once and cannot be changed. Which is good because we can replace with environment specific variables that are stored elsewhere.

Why?

Good practices and methodologies ask for us to decouple configuration from code.

Why?

Configuration changes, environment to environment and code does not.

An environment variable is made up of a name/value pair, and any number may be created and available for reference at a point in time.

Create and configure the .env file

.env is just a file that works pretty much list of variables 📜 we want our environment to have. This allows us to separate our secrets and configuration constants from our source code.

Even though you can configure it to be anywhere, you should place in root of the project.

$ touch .env

Now we can edit our .env file to add our variables following this rules:

  • Variables are created following a KEY=VALUE pattern.
  • All values assigned to environment variables are represented as strings when they are accessed in the code. That means a variable assigned as BOOLEAN=true will have the value of 'true'.
  • No spaces after the equal sign. If spaces are required for the value, then quotes can be used around it.
# .env
MEANING_OF_LIFE=42
SUPER_SECRET_PASSWORD=123456
SUPER_SECRET_USERNAME=nomade55

🧱 About building

Variables are injected into applications at build/compile time. This means servers whether they local or not, need to be restarted. Application has to be re-built in order to see changes made to values in .env files.

Keeping our file away remote repositories

We've defined a whole file with important configuration variables we intend to keep secret, so the most logical thing is adding it to our .gitignore. This way, Git will not track this file and prevent it from reaching (for example) a public repository.

# .gitignore
.env

Accessing our variables

Getting the values of our variables comes down to what you are developing your applications with. I'm leaving examples for Python, Javascript and PHP (which are the language I use the most). Although there are plenty of libraries and resources available to read variables stored in .env files for almost any language and framework.

🌐 Javascript (using node)

Install dotenv library to read your .env through your favorite node package manager.

$ npm install dotenv
# or with Yarn 
$ yarn add dotenv
# or with pnpm
$ pnpm install dotenv
// Early as possible in the application, require and configure dotenv.
require('dotenv').config();
// Now the process.env will have the variables we assigned in your .env file.
const meaningOfLife = process.env.MEANING_OF_LIFE
connectDB({
    username: proccess.env.SUPER_SECRET_USERNAME,
    password: proccess.env.SUPER_SECRET_PASSWORD
});

⚠️ If you are using a framework like Vue or React, the dotenv library and its implementation comes already ready to use the framework.

🐘 PHP

We first need to Install a dotenv library to read the .env file and populate the superglobals in your PHP Application with composer. I suggest symfony/dotenv.

$ composer require symfony/dotenv
# This should be added as soon as you can in the code.
$dotenv = new Symfony\Component\Dotenv\Dotenv();
$dotenv->load(__DIR__.'/.env');

# Then its possible to use getenv() to call the previously defined environment variables.
$meaning_of_life = getenv('MEANING_OF_LIFE');
# Or also
$username = $_ENV('SUPER_SECRET_USERNAME');
$password = $_ENV('SUPER_SECRET_PASSWORD');

⚠️ Important thing to note, when using a framework like Symfony or Laravel, it isn't needed to add any library. This comes already out of the box with the framework.

  • Laravel

      # The second parameter works as a default in case the env variable is not set.
      $meaning_of_life = env('MEANING_OF_LIFE', null);
    
  • Symfony

      $meaning_of_life = getenv('MEANING_OF_LIFE');
    

🐍 Python

Install a dotenv library to read your .env file through pip. I suggest python-dotenv.

$ pip install python-dotenv
# This should be added as soon as you can in the code.
import os
from dotenv import load_dotenv

# Now we can get call previously defined environment variables.
MEANING_OF_LIFE = os.environ.get('MEANING_OF_LIFE')
USERNAME = os.environ.get('SUPER_SECRET_USERNAME')
PASSWORD = os.environ.get('SUPER_SECRET_PASSWORD')

Collaborating with others

These environment variables are required to run our application successfully. So if we intend others to run the application in their machine, we need to leave a template or example file. This will allow them to configure the application, with their own set of secret environment variables.

# .env.template
MEANING_OF_LIFE=
SUPER_SECRET_PASSWORD=
SUPER_SECRET_USERNAME=

APP_NAME="New Awesome Million Dolar Project"
APP_VERSION="0.0.2"

We leave only the secret variables empty 🕵️. This way, new users will copy this file, rename it to .env and fill the configuration variables needed to run the application on their side.

We want git to track and version this file, so remember to add, commit and push this file to your remote repository!

Parting words

Environment variables are very useful to keep the configuration decoupled from the code, but also keeping our secrets... well... secret! This is not the only thing environment variables are good for, they occupy a very important role when we develop with different environments. That topic is oh so far way from the scope I intended for this post though.

Hope this gives a you a new insight on what is a .env file and how to use them. If you have any question, on environment variables or feedback for this article, go ahead and comment below.

I also welcome suggestions for new articles and reactions 🦄.

Until the next one 🧉!