Environment variables for novice developers or using an .env file in software development

Environment variables for novice developers or using an .env file in software development

What awaits you in this guide

Safely managing sensitive data and application settings can seem like a daunting task for novice developers. In this article, we will discuss important aspects of working with environment variables that will make your development safer and more efficient.

It is important to note that the considered solution is not the best way to store secret data, but the importance of understanding the principles of working with environment variables is difficult to overestimate.

Let’s start!

What are environment variables?

Environment variables are values ​​that are used by various commands and software scripts that are executed in the operating system. In principle, they work in the same way as variables in programming languages. They represent the familiar “key-value” pairs and are used to store parameters, application settings, store keys and other information data.

First look and practice

Let’s find out what environment variables are already in your operating system.

Command examples are presented for the Windows command line and the Unix shell (similar) operating systems separately.

CMD:

set

Bash:

printenv

This way you will see a list of variables that already exist in the environment.

Look closely at the environment variable with the name PATH. It is very important and contains paths to directories containing executable program files. When we refer to different software using its name:

node main.js

Executable file named node (or node.exe for Windows) must be in the directories listed in the environment variable PATH. If it cannot be found, you will receive an error message:

command not found: node

or

"node" is not recognized as an internal or external command, operable program or batch file.

If node.js is already installed on the operating system, but you want to read the error message from the example, use instead node another character set that will not match any executable file in the directories listed in PATH. Example, foo, bar or not at all abracadabra123.

If you already have the app you want installed, but its location is missing from the variable PATHyou can use the full file path in the command:

CMD:

"C:\Program Files\nodejs\node.exe" main.js

Bash:

# пример для nvm
/root/.nvm/versions/node/v16.18.0/bin/node main.js 

On the contrary, if the program is installed and its location is in the variable PATHYou can find out its exact location as follows:

CMD:

where node

Bash:

which node

Creating, using, and deleting environment variables

Let’s try to create a new variable:

CMD:

# Переменная доступна только для текущего сеанса командной строки
set MSG=hello

# Необходим перезапуск сессии командной строки
# Такая переменная останется доступной даже после перезапуска операционной системы 
setx MSG hello /M /D

Bash:

# Переменная доступна только для текущего сеанса терминала:
MSG=hello

# Необходим перезапуск сессии терминала,
# но переменная доступна для всех дочерних процессов 
export MSG=hello

# Чтобы сделать переменную доступной после перезагрузки операционной системы,
# необходимо добавить ее объявление в соответствующий файл конфигурации вашей оболочки
# .bashrc для Bash
echo 'export MSG=hello' >> ~/.bashrc
source ~/.bashrc

# .zshrc для Zsh
echo 'export MSG=hello' >> ~/.zshrc
source ~/.zshrc

Note that since environment variables are constants, it is common practice to write their names in uppercase.

Let’s try to use a new variable:

CMD:

echo %MSG%

Bash:

echo $MSG

Now we delete the variable:

CMD:

set MSG=
setx MSG ''

Bash:

unset MSG
# Также не забывайте удалить объявление переменной из файла
# конфигурации оболочки, если она была объявлена в нем. В противном
# случае, после перезапуска операционной системы переменная снова
# будет создана

Confidential data in the application

Our software uses a variety of confidential information to function properly. Such information may appear API/SSH key, password to connect to the database, ssl certificate or something similar. If we put this data inside our public source code repository, it will be compromised. Even if the repository is private, the location of classified information means that all its collaborators or users have access to the classified information.

However, we can avoid such risks by moving sensitive information out of storage and replacing it with links to a place where it is securely stored. This approach provides a high level of security, as each user will only have access to the location where confidential data is stored. For example, each user can have their own password to a local database, and their link will point to the personal repository with that password.

Using external repositories for sensitive data allows us to effectively manage access to this information and prevent unauthorized access. In addition, it makes team collaboration safer. This approach also facilitates the rotation of keys and passwords without the need to change the application code, which increases the level of protection and reduces risks to data security.

Storing secret data in environment variables

Let’s consider environment variables as a storage of secret data.

Let’s imagine that our application needs a connection to the database. In this case, we will create several environment variables, placing in them the necessary information on the server:

Server

echo 'export DB_HOST=srv_db_host' >> ~/.bashrc
echo 'export DB_PORT=srv_db_port' >> ~/.bashrc
echo 'export DB_NAME=srv_db_name' >> ~/.bashrc
echo 'export DB_USER=srv_db_user' >> ~/.bashrc
echo 'export DB_PASSWORD=srv_db_password' >> ~/.bashrc

source ~/.bashrc

The developer’s workplace

echo 'export DB_HOST=local_db_host' >> ~/.bashrc
echo 'export DB_PORT=local_db_port' >> ~/.bashrc
echo 'export DB_NAME=local_db_name' >> ~/.bashrc
echo 'export DB_USER=local_db_user' >> ~/.bashrc
echo 'export DB_PASSWORD=local_db_password' >> ~/.bashrc

source ~/.bashrc

WARNING! Placing environment variables in the bashrc file without restricting access to it is unacceptable, the information is provided for demonstration purposes only!

Now we can access these variables inside the program:

Node.js

/*
process.env является глобальной переменной внутри node.jsприложения.
Она хранит в себе состояние всех переменных окружения ОС
на момент запуска приложения.
*/

const dbHost = process.env.DB_HOST;
const dbPort = process.env.DB_PORT;
const dbName = process.env.DB_NAME;
const dbUser = process.env.DB_USER;
const dbPassword = process.env.DB_PASSWORD;

Python

'''
В языке программирования python доступ к переменным окружения
возможно получить с использованием модуля OS. 
'''

import os

db_host = os.environ['DB_HOST']
db_port = os.environ['DB_PORT']
db_name = os.environ['DB_NAME']
db_user = os.environ['DB_USER']
db_password = os.environ['DB_PASSWORD']

Golang

/* 
в языке программирования Go доступ к переменным окружения осуществляется
c помощью пакета OS.
*/

package main

import (
	"os"
)

func main() {
	dbHost := os.Getenv("DB_HOST")
	dbPort := os.Getenv("DB_PORT")
	dbName := os.Getenv("DB_NAME")
	dbUser := os.Getenv("DB_USER")
	dbPassword := os.Getenv("DB_PASSWORD")
}

Using an .env file to store variables

We previously looked at how sensitive information can be stored directly in the operating system environment. This method has some disadvantages. These variables are not only available to the program.

There is also some inconvenience in their administration: they are mixed with environment variables that our application does not need. The process of replacing variable values, in turn, is also very convenient.

Can we separate the required variables from the others, make them available only to the application, and manage their state in a convenient way?
Yes, we can use practices that have become the gold standard for solving these problems. It consists in the use of special libraries that allow you to add a number of variables to its scope when the program is launched, which will remain inaccessible to other processes in the operating system. Such libraries are present in all popular development stacks and use as repositories a special file designed in a strictly certain way. The name of this file .env (From the English Environment).

.env file syntax

An .env file is a plain text file with no name extension .enveach row of which is a pair key = value. Here is an example of writing variables to such a file:

DB_HOST=local_db_host
DB_PORT=local_db_port
DB_NAME=local_db_name
DB_USER=local_db_user
DB_PASSWORD=local_db_password

Let’s link this file to our application using the libraries appropriate for the required development stack:

Node.js:

/*
Установим библиотеку dotenv при помощи пакетного менеджера:
npm install dotenv
или
yarn add dotenv
*/

const dotenv = require('dotenv');
dotenv.config();

const dbHost = process.env.DB_HOST;
const dbPort = process.env.DB_PORT;
const dbName = process.env.DB_NAME;
const dbUser = process.env.DB_USER;
const dbPassword = process.env.DB_PASSWORD;

/* 
Объявленные переменные содержат соответствующие значения из .env файла.
Node.js версии 20 предоставляет экспериментальную поддержку .env файлов
без использования библиотеки "dotenv":
node --env-file=.env app.js
*/

Python:

# Установим пакет python-dotenv с помощью pip
# pip install python-dotenv

import os
from dotenv import load_dotenv

load_dotenv()

db_host = os.getenv("DB_HOST")
db_port = os.getenv("DB_PORT")
db_name = os.getenv("DB_NAME")
db_user = os.getenv("DB_USER")
db_password = os.getenv("DB_PASSWORD")

# Объявленные переменные содержат значения из .env файла и могут быть использованы

Golang:

/*
Установим пакет github.com/joho/godotenv с помощью go get
go get github.com/joho/godotenv
*/

package main

import (
	"github.com/joho/godotenv"
	"os"
)

func main() {
	err := godotenv.Load()

    if err != nil {
        log.Fatalf("Error loading .env file")
    }

	dbHost := os.Getenv("DB_HOST")
	dbPort := os.Getenv("DB_PORT")
	dbName := os.Getenv("DB_NAME")
	dbUser := os.Getenv("DB_USER")
	dbPassword := os.Getenv("DB_PASSWORD")

    // Объявленные переменные содержат значения из .env файла и могут быть использованы
}

WARNING! One of the most important purposes of use .env in the project – there is no confidential data in the repository. Don’t forget to take care of adding .env in .gitignoreif you use git as a version control system. If you are using an alternative version control system, exclude .env in accordance with the technical documentation.

Place in .gitignore:

# Excluding the 
.env file.env

This approach of storing confidential information can be observed in most large frameworks or libraries.

  • Laravel — one of the most popular frameworks for developing PHP web applications. File .env in Laravel used to store database settings, application settings, secret keys, email settings, and other configuration options. Laravel automatically loads environment variables from a file .env when running the program.

  • Django is a framework for developing web applications in Python. In projects Django also a common use of the file .env to save database settings, secret keys, email settings and other configuration options. To load environment variables from a file .env in Django the python-dotenv library is commonly used.

  • Ruby on Rails – A framework for developing web applications on Ruby. In projects Ruby on Rails also a popular use of the file .env to save configuration settings, including database settings, API keys, and other settings. To load environment variables from a file .env in Ruby on Rails the dotenv-rails library is usually used.

In practice, many developers use the file .env in your projects to easily configure and store environment settings and sensitive data without having to display them in your code.

.env.example

Creating a file with a name is considered good manners .env.example, which should store the list of necessary filling variables. This file is used to document and provide a possible example of populating environment variable values. Please note, it can reside in the repository, but must not contain this sensitive information.

Conclusion

This article discussed important aspects of handling sensitive information in applications. We got acquainted with the most common way of hiding confidential information from software repositories.

Remember that the secure handling of sensitive data is an important aspect of software development. Using the file .env combined with well-organized environment variables, helps to protect information and simplifies the configuration of applications for different environments.

Thank you for attention!

Related posts