Basic App Security for Beginners with Environment Variables

I'm learning about application development, and I rarely see beginner topics discuss security for an application as you begin development. I've found that using environment variables to keep your secrets safe is an effective way to use source control and not commit your passwords or expose other material you want to keep private.

What should go in these environment variables? So far I've included:

  • Account Name
  • Account Password
  • Connection Strings
  • Environment based configuration properties
    • Development properties not used in production for example

Environment variables in what I've found appear to be a standard approach in both Linux and Windows environments. Reading from environment variables also seems to be commonly supported across many languages, including two I'm working with now, Python, and Javascript. I've included a Python sample that I use today to avoid putting my passwords directly into my files. To follow along, I've created a GitHub Repository with the sample code reviewed below. Beyond the core libraries, I'll also be using python-dotenv.

Python .env Sample Project

Project Layout

.env
.env.example
.gitattributes
.gitignore
HelloLothric.py
LICENSE.md
README.md
requirements.txt

I've put .env in gray to indicate this is NOT committed to source control by including .env* in my .gitignore (Official Documentation) file. I also add !.env.example to the same file to explicitly include the example file so that others know how to configure their .env files.

.env & .env.example

I keep the style of the two files in sync, with .env.example containing the expected values, and .env containing the real ones.


DB_USER_NAME="Solaire" #Username to connect to the database
DB_USER_PASSWORD="UHJhaXNlVGhlU3VuIQ==" #Base64 encoded password for obfuscation
DB_HOST="Astora" #Server running the database
DB_NAME="Lordran" #Database name on server

HelloLothric.py

import os
import base64
from dotenv import load_dotenv

load_dotenv()

db_connection_params = {
    'host':os.getenv("DB_HOST"),
    'user':os.getenv("DB_USER_NAME"),
    'password':base64.b64decode(os.getenv("DB_USER_PASSWORD")).decode("utf-8"),
    'database':os.getenv("DB_NAME")
}

print(f'{db_connection_params["user"]} has connected to Database {db_connection_params["database"]} on {db_connection_params["host"]}')

Result

Solaire has connected to Database Lordran on Astora

This is a trivial example, but you could easily switch the print to be a database connection, and now your code pulls it's configuration from a file that is not exposed in source control, or directly embedded in your code.

I've also made use of base 64 encoding for two purposes. It ensures that all the formatting of the text is handled in such a way that it will remain consistent in transit. It also provides a way to obfuscate the password, so that as your partner looks at the screen, they are not seeing the raw password. This is easily decoded, and is not a form of security via encryption. By default when the value is decoded, it's returned in Bytes rather than as a String, which is expected by the database connection. On unix you can make use of os.getenvb() which handles bytes, or to make it run anywhere, I decode it to a UTF-8 string.

As part of researching this topic, Scott Hanselman had a great post for those developing in C# / ASP.NET / Azure. I also came across The Twelve-Factor App, a guide on how to develop modern applications. Among those 12 sections is Config and how to handle situations like these. They also recommend environment variables, or env vars, as an approach to secure your application while conforming to the environment it's developed in. Further reading in this space, or when you are preparing to put your code into production, I would advise reading up on things like Key Vaults, or Secret Management services to be used as you deploy your application to a production environment. As of this time GitHub is also starting to roll out Actions, which includes a way to manage secrets.

Author image
Hi, I'm Aaron Grossman, a Business Intelligence developer documenting what I've learned as I continue to grow my career. I can be reached at me@aaronjgrossman.com.