Keep your code secure by using environment variables and env files

Securely load a file containing all of our app’s required, confidential data like passwords, tokens, etc

Keep your code secure by using environment variables and env files
Let’s enrich this beautiful environment with some variables (image by Rob Morton on Unsplash)

Your code needs to connect to a database. For this it needs a password to that database. How can you use this password in your app in such a way that you can both connect to the database and keep the password private?

This article shows you how to use an env file in your code. This file stores all of our confidential information. It can be loaded once so you have access to all of your private information like passwords anywhere in your app. In this article we’ll use Python to illustrate the workings of an env file but the principle applies for all programming languages.

At the end of this article you’ll:

  • understand the advantages of using environment variables
  • understand how environment variables work
  • understand how we can load environment variables from an env file

Let’s code!


1. Why use environment variables?

There are two main reasons to use environment variable files:
1. safety
2. flexibility

Keep your credentials safe

The first reason is the most important by far: environment variables keep your credentials save. We want to prevent our code being riddled with passwords and other information that other shouldn’t know. Not only is it bad practice; it’s also very dangerous, especially if you upload your code to a public repository like GitHub. Then you just publicly present your credentials to anyone who comes across it!

Environment variables collect all of our confidential information in a single file. Since all of our private info is collected in a single file we can easily gitignore this single file to prevent our data from being pushed to our git repository!

Flexibility

Environment files can be modified externally. If your database password changes we can just open the file containing all the environment variables,
change the database password and restart our app. This is much more convenient than searching for, and then rewriting the hardcoded passwords within the app.

In addition you can run a program (e.g. in Docker) and ‘feed’ it an env file (like in the article below). This makes it very easy to switch between an development and production database for example.

A complete guide to using environment variables and files with Docker and Compose
Keep your containers secure and flexible with this easy tutorial

2. How environment variables work

Now that we’re convinced that using env vars is smart, let’s figure out how they work. Every operating system (Windows, Linux, Mac) creates a set of environment variables for each process it creates. An example of one of these processes can be a script we’re running.

I think of an env file as a key for my app (image by Matt Artz on Unsplash)

We’ll start with reading these variables and then add some variables of our own. The examples below are written in Python because this is a very accessible language but please notice that env vars are not exclusive to Python: functionalities like below are present in every type of programming language.

Reading environment variables

Let’s first check out which variables already exist;import os
for key, value in os.environ.items():
 print(key, value)

We access the env vars with os.environ. This returns a dictionary. Using the items() method we can loop through all the keys and values and print them. In my case this prints out a lot of information about my system:

  • locations of my PROGRAMFILES, PATH and APPDATA
  • VIRTUAL_ENV information
  • my USERNAME and COMPUTERNAME
  • etc

We can also read one specific variable, for example my COMPUTERNAME:import os
print(os.environ.get(“COMPUTERNAME”))

Creating an environment variable

Since os.environ is a dictionary we can easily add new variables. First we’ll create a variable with the key `HELLO` and the value `WORLD`. Then we’ll retrieve the value againimport os# Create an env var
os.environ['HELLO'] = 'WORLD'# Retrieve the newly created var
print(os.environ.get("HELLO"))

This print out `WORLD`. Easy! Also try to read this variable from somewhere else in the app and notice that it’s accessible everywhere.

Let’s keep those passwords safe (image by Markus Winkler on Unsplash)

3. Loading env vars into the env from an env file

Now that we understand the environment and how to use environment variables it’s easy to see what the environment file does; it specifies some extra pairs of keys and values that we want to load into our environment. In this section we’ll create an env file and then load it into the environment in its entirety.

a. Creating the env file

We’ll first create a file called `.env` at this location of our app: /config/conf/.env. The content of the env file looks like this:DB_TYPE = postgresql
DB_USER = postgres
DB_PASS = mikepw
DB_HOST = localhost
DB_NAME = my_webshop_db

b. Loading the env file

Next we need to load this file into our app environment. Most programming languages have packages of built-in functionalities to load these kinds of files.
Most convenient for Python is to install a package: pip install python-dotenv. This package conveniently loads the values in the .env file in to the Python Environment. Then simply call the load_dotenv function and pass the location of the .env file.

The location of the .env file → working with relative paths in Python
The load_dotenv function needs an absolute path to the location of the .env file. In this article we’ll just pass it hardcoded but once we deploy our code we could face a problem here: on my machine the env file is located at is c:/users/mike/envfileproject/config/conf/.env but on your machine it might be c:/pythonstuff/envfileproject/config/conf/.env. Check out the article below for an easy, reusable trick to solve the headaches that come with calculating the absolute path to our env file.

Simple trick to work with relative paths in Python
Calculate the file path at runtime with ease

c. Loading the env file

We’ll use the python-dotenv package and the ROOT_DIR to load the .env file into the environment:if (os.environ.get("DB_TYPE") == None):
from dotenv import load_dotenv
from config.definitions import ROOT_DIR
load_dotenv(os.path.join(ROOT_DIR, 'config', 'conf', '.env'))

First we’ll check if one of our required variables (in this specific case DB_TYPE) is already loaded in the environment. This is the case when we pass the env file to this Python project externally, through Docker e.g. Check out the article below on how to work with env files in Docker.

A complete guide to using environment variables and files with Docker and Compose
Keep your containers secure and flexible with this easy tutorial

If the DB_TYPE variable doesn’t exist in the environment yet we’re going to load it using the python-dotenv package.

  1. import the load_dotenv function from dotenv (this is the python-dotenv package we’ve installed earlier)
  2. Import the ROOT_DIR from /config/definitions.py
  3. Pass the path to the .env file to the load_dotenv function
  4. DONE!

Our env file is now loaded into Python. We can securely and conveniently access all the values by requesting the key like this:database_password = os.environ.get("DB_PASS")

For a full example on how to impolement an .env file in a Docker container check out the article below. It it we use an env file to pass data to a Postgres container:

Getting started with Postgres in Docker
Creating a Postgres database in a Docker container for beginners

Conclusion

In summary: environment files offer us security and flexibility by storing all of our confidential information in a single file that we can load in our code to get secure access to all of the required values.

I hope to have shed some light on the inner workings of environment variables and how to work with environment files. If you have suggestions or clarifications please comment so I can improve this article. In the meantime, check out my other articles on all kinds of programming-related topics like these:

Happy coding!

— Mike

P.S: like what I’m doing? Follow me!

Join Medium with my referral link - Mike Huls
As a Medium member, a portion of your membership fee goes to writers you read, and you get full access to every story…