Avoid Putting Credentials in Code
by Vee H. Phan
Recently I was working on a codebase that had database credentials hardcoded in connection strings. Not only is the connection string hardcoded with credentials, but it is also repeated in multiple places in the codebase. Two sins are committed here:
1. Hardcoding credentials in the codebase
This is a bad practice because it exposes sensitive information such as database credentials. Hardcoding credentials in the codebase makes it easy for attackers to access the database and steal sensitive information. This is a security risk and should be avoided.
2. Repeating the same connection string in multiple places
One of the basic principles of software development is DRY (Don't Repeat Yourself). Repeating the same code, in this case a connection string, in multiple places violates this principle. If the connection string needs to be changed, it would have to be changed in multiple places. This is not only time-consuming but also error-prone.
Solution: Use environment variables
Use a .env file in a project codebase
A method to prevent hardcoding credentials in the codebase is to utilize environment variables. One approach to set environment variables is by using a .env file within the project. This file contains key-value pairs of environment variables and should not be included in version control. To ensure this, add it to the .gitignore file.
In the root directory of the codebase, the .env file can be created with the environment variables. Sample .env file with database credentials:
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=password
DB_NAME=mydatabase
DB_CONNECTION_STRING=postgressql://$DB_USER:$DB_PASSWORD@$DB_HOST/$DB_NAME
To load the environment variables from the .env file, there are a number of python package can be used. They are:
python-dotenv
python-decouple
envparse
My go-to package is python-dotenv
. The python-dotenv
package is a Python library that loads environment variables from a .env file into the os.environ dictionary. The python-dotenv
package is easy to use and works well with Python projects. To use the python-dotenv
package, it can be installed using pip:
pip install python-dotenv
import os
from dotenv import load_dotenv
load_dotenv()
db_host = os.getenv("DB_HOST")
db_user = os.getenv("DB_USER")
db_password = os.getenv("DB_PASSWORD")
db_name = os.getenv("DB_NAME")
connection_string = os.getenv("DB_CONNECTION_STRING")
This way, the credentials are not hardcoded in the codebase and can be changed easily in the .env file. The connection string is defined in one place and can be reused in multiple places within the same codebase.
Use a .env file located outside the codebase
One can also store the .env file in a secure location and load the environment variables from the .env file using the load_dotenv(dotenv_path="path/to/.env")
function. This way, the .env is located outside the codebase and will not be checked into version control. Other codebase within the same machine can also use the same .env file to load the environment variables, effectively one only needs one .env file for multiple codebases.
import os
from dotenv import load_dotenv
load_dotenv(dotenv_path="/path/to/.env")
In this case, the path to the .env file must be specified in the load_dotenv()
function. The disadvantage is that the path to the .env file must be specified in the codebase. If the path changes, the codebase must be updated.
Set the environment variables in the .bashrc or .bash_profile file
Another way to set environment variables is to set them in the .bashrc or .bash_profile file. The .bashrc file is a shell script that is executed when a new terminal is opened. The environment variables can be set in the .bashrc file using the export command.
To add the environment variables to the .bashrc or .bash_profile file, open the file in a text editor and add the export command for each environment variable. For example, to set the DB_HOST environment variable, add the following lines to the .bashrc or .bash_profile file:
export DB_HOST=localhost
export DB_USER=root
export DB_PASSWORD=password
export DB_NAME=mydatabase
export DB_CONNECTION_STRING=postgressql://$DB_USER:$DB_PASSWORD@$DB_HOST/$DB_NAME
After adding the environment variables to the .bashrc or .bash_profile file, save the file and run the source command to apply the changes to the current shell session. The source command reads and executes the commands in the .bashrc or .bash_profile file in the current shell session. To run the source command, open a terminal and run the following command:
source ~/.bashrc
The environment variables are set automatically each time a new terminal is opened, and they are available to all codebases. The environment variables can be accessed in the codebase using the os.environ dictionary. For example, to access the DB_CONNECTION_STRING environment variable, use the following code:
import os
CONN_STR = os.environ.get("DB_CONNECTION_STRING")
Finally, configurations pertaining to a project can be stored within a configuration file, such as a settings.py file, and the environment variables can be loaded in the settings.py file using the os.environ
dictionary. All other modules can then import the connection string (or any other configurations) from the settings.py file.
Conclusion
Avoid hardcoding credentials in the codebase. Use environment variables to store credentials and other sensitive information. Environment variables are a secure way to store sensitive information and make it easy to change the information in the future. Environment variables can be set in a .env file in the codebase, or in a secure location outside the codebase, or in the .bashrc file. By using environment variables, the codebase is more secure and easier to maintain.
Subscribe to my newsletter
Read articles from Vee Huen Phan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by