Keep your code secure by using environment variables and env files | by Mike Huls | Towards Data Science

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!
There are two main reasons to use environment variable files:  1. safety 2. flexibility
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!
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 this article). This makes it very easy to switch between an development and production database for example.
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.
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.
Let’s first check out which variables already exist;
import osfor 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 osprint(os.environ.get(“COMPUTERNAME”))
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 again
import os# Create an env varos.environ['HELLO'] = 'WORLD'# Retrieve the newly created varprint(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.
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.
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 = postgresqlDB_USER = postgresDB_PASS = mikepwDB_HOST = localhostDB_NAME = my_webshop_db
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 PythonThe 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 this article for an easy, reusable trick to solve the headaches that come with calculating the absolute path to our 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 this article on how to work with env files in Docker or this article on how to spin up a Docker Postgres container.If the DB_TYPE variable doesn’t exist in the environment yet we’re going to load it using the python-dotenv package.
import the load_dotenv function from dotenv (this is the python-dotenv package we’ve installed earlier)Import the ROOT_DIR from /config/definitions.pyPass the path to the .env file to the load_dotenv functionDONE!
import the load_dotenv function from dotenv (this is the python-dotenv package we’ve installed earlier)
Import the ROOT_DIR from /config/definitions.py
Pass the path to the .env file to the load_dotenv function
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")
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:
Getting started with Postgres in Docker
Create a fast auto-documented, maintainable and easy-to-use Python API in 5 lines of code with FastAPI
Python to SQL — UPSERT Safely, Easily and Fast
Create and publish your own Python package
Create Your Custom, private Python Package That You Can PIP Install From Your Git Repository
Virtual environments for absolute beginners — what is it and how to create one (+ examples)
Dramatically improve your database insert speed with a simple upgrade
Happy coding!
— Mike
P.S: like what I’m doing? Follow me!