This guide outlines best practices to securely handle and obfuscate a plain-text password in Python.
The approach uses the cryptography package to encrypt the password and environment variables to store the encrypted password, preventing others from viewing it in plain text.
Step-By-Step Guide¶
1) Avoid Hardcoding Plain-Text Passwords
2) Install Required Packages
3) Encrypt The Password
4) Load and Decrypt The Password
Step 1) Avoid Hardcoding Plain-Text Passwords¶
Hardcoding passwords in code is insecure, as it exposes sensitive data to anyone with access to the codebase.
Example of Insecure Passwords:
PLAINTEXT_PASSWORD = "This is a security risk."
Step 2) Install Required Packages¶
Install the cryptography package for encryption and python-dotenv for loading environment variables.
pip install cryptography python-dotenv
Step 3) Encrypt The Password¶
Encrypt the password using the cryptography package to obfuscate it, ensuring it’s not stored in plain text.
Step 3.1) Create an Encryption Script¶
Create a script to encrypt the password and generate an encryption key. The encrypted password will be stored in the .env
file, and the key will be saved separately.
encrypt_password.py:
from cryptography.fernet import Fernet
import os
# Prompt for the plain-text password (or load from a secure source)
password = input("Enter your password: ")
if not password:
raise ValueError("Password cannot be empty.")
# Generate a key for encryption
key = Fernet.generate_key()
fernet = Fernet(key)
# Encrypt the password
encrypted_password = fernet.encrypt(password.encode())
# Create or update the .env file with the encrypted password
with open(".env", "w") as env_file:
env_file.write(f'ENCRYPTED_PASSWORD="{encrypted_password.decode()}"\n')
# Save the key to a separate file
with open("key.txt", "wb") as key_file:
key_file.write(key)
print("Password encrypted successfully. Encrypted password stored in .env, and key saved to key.txt.")
Security Notes:
- Run this script once to encrypt the password and generate key.txt and .env.
- Store key.txt securely (e.g., with restricted permissions:
chmod 600
key.txt on Unix-like systems.- The encrypted password in .env is safe to commit to version control, as it requires the key to decrypt.
- Do not store the plain-text password in the codebase or any file after encryption.
Step 3.2) Create a .env File¶
The encrypt_password.py script automatically creates or updates the .env file with the encrypted password. Ensure the .env file exists in your project directory with the following content:
ENCRYPTED_PASSWORD=your_encrypted_password
- The your_encrypted_password value is a base64-encoded string generated by the encryption script (e.g., gAAAAAB...).
- Add .env to your
.gitignore
file as a precaution, though the encrypted password is secure:.env
- Add key.txt to .gitignore to prevent committing the encryption key:
key.txt
Step 4) Load and Decrypt The Password¶
Use the python-dotenv package to load the encrypted password from the .env file and decrypt it using the cryptography package in your Python script.
Example:
from cryptography.fernet import Fernet
from dotenv import load_dotenv
import os
# Load the encrypted password from .env
load_dotenv()
encrypted_password = os.getenv("ENCRYPTED_PASSWORD")
if not encrypted_password:
raise ValueError("Encrypted password not found in .env file. Please set ENCRYPTED_PASSWORD.")
# Load the encryption key
try:
with open("key.txt", "rb") as key_file:
key = key_file.read()
except FileNotFoundError:
raise FileNotFoundError("Encryption key not found. Ensure key.txt exists.")
# Decrypt the password
fernet = Fernet(key)
password = fernet.decrypt(encrypted_password.encode()).decode()
# Use the password (e.g., for API authentication)
# Example: client = api_client.Client(username="your_username", password=password)
# client.login()
# Clear password from memory
password = None
import gc
gc.collect()