Encrypt sensitive values in environment files using AES-GCM
Project description
EnvSeal
Encrypt sensitive values in environment files using AES-GCM
EnvSeal allows you to store encrypted values in your environment files (like .env) instead of plain-text secrets. It uses industry-standard AES-GCM encryption and provides flexible options for managing your master passphrase.
Features
- 🔒 Strong Encryption: AES-GCM with Scrypt key derivation
- 🔑 Flexible Passphrase Sources: OS keyring, environment variables, .env files, hardcoded, or interactive prompt
- 🐍 Easy Python Integration: Works seamlessly with python-dotenv
- 💻 Cross-Platform: Works on Windows, macOS, and Linux
- 🛠️ CLI & Library: Use from command line or import as a Python library
- 📁 Bulk File Processing: Encrypt all values in a file or only specific marked values
- 📦 No External Dependencies: Only requires cryptography and optional keyring/python-dotenv
Installation
pip install envseal
Optional dependencies:
# For OS keyring support
pip install envseal[keyring]
# For .env file support
pip install envseal[dotenv]
# For development
pip install envseal[dev]
Quick Start
1. Encrypt a value
# Using CLI with keyring (most secure)
envseal store-passphrase "my-super-secret-passphrase"
envseal seal "my-database-password"
# Output: ENC[v1]:eyJzIjoiNnZ...
# Or with environment variable
export ENVSEAL_PASSPHRASE="my-super-secret-passphrase"
envseal seal "my-database-password" --passphrase-source=env_var
2. Encrypt an entire file
# Encrypt all values in a .env file
envseal seal-file .env
# Only encrypt values marked with ENC[v1]: prefix (selective encryption)
envseal seal-file .env --prefix-only
# Create a backup before modifying
envseal seal-file .env --backup
# Output to a different file
envseal seal-file .env --output .env.sealed
3. Add to your .env file
DATABASE_URL=postgresql://user:password@localhost/db
DB_PASSWORD=ENC[v1]:eyJzIjoiNnZ...
API_KEY=ENC[v1]:eyJzIjoiOXR...
4. Use in your Python application
import os
from envseal import load_sealed_env, PassphraseSource
# Load and decrypt all environment variables
env_vars = load_sealed_env(
dotenv_path=".env",
passphrase_source=PassphraseSource.KEYRING
)
# Access decrypted values
db_password = env_vars["DB_PASSWORD"]
api_key = env_vars["API_KEY"]
# Or apply directly to os.environ
from envseal import apply_sealed_env
apply_sealed_env(".env", PassphraseSource.KEYRING)
db_password = os.environ["DB_PASSWORD"]
Usage Examples
CLI Usage
Load environment files
# Load and display decrypted .env file
envseal load-env --env-file=.env
# Apply to current environment
envseal load-env --env-file=.env --apply
Python Library Usage
Basic encryption/decryption
from envseal import seal, unseal, get_passphrase, PassphraseSource
# Get passphrase from keyring
passphrase = get_passphrase(PassphraseSource.KEYRING)
# Encrypt
token = seal("my-secret-value", passphrase)
print(token) # ENC[v1]:eyJzIjoiNnZ...
# Decrypt
plaintext = unseal(token, passphrase)
print(plaintext.decode()) # my-secret-value
File encryption
from envseal import seal_file, get_passphrase, PassphraseSource
# Get passphrase
passphrase = get_passphrase(PassphraseSource.KEYRING)
# Encrypt all values in a file
modified_count = seal_file(
file_path=".env",
passphrase=passphrase,
prefix_only=False # Encrypt all values
)
print(f"Encrypted {modified_count} values")
# Only encrypt values marked with ENC[v1]: prefix
modified_count = seal_file(
file_path=".env",
passphrase=passphrase,
prefix_only=True # Only encrypt marked values
)
print(f"Encrypted {modified_count} marked values")
# Output to different file
seal_file(
file_path=".env",
passphrase=passphrase,
output_path=".env.sealed"
)
Working with .env files
from envseal import load_sealed_env, PassphraseSource
# Load with automatic decryption
env_vars = load_sealed_env(
dotenv_path=".env",
passphrase_source=PassphraseSource.KEYRING
)
# Access values
db_password = env_vars.get("DB_PASSWORD")
api_key = env_vars.get("API_KEY")
Different passphrase sources
from envseal import get_passphrase, PassphraseSource
# From OS keyring
passphrase = get_passphrase(PassphraseSource.KEYRING)
# From environment variable
passphrase = get_passphrase(
PassphraseSource.ENV_VAR,
env_var_name="MY_PASSPHRASE"
)
# From .env file
passphrase = get_passphrase(
PassphraseSource.DOTENV,
dotenv_path=".secrets.env",
dotenv_var_name="MASTER_KEY"
)
# Hardcoded (not recommended for production)
passphrase = get_passphrase(
PassphraseSource.HARDCODED,
hardcoded_passphrase="my-passphrase"
)
# Interactive prompt
passphrase = get_passphrase(PassphraseSource.PROMPT)
Integration with python-dotenv
import os
from dotenv import load_dotenv
from envseal import unseal, get_passphrase, PassphraseSource
# Load .env file normally
load_dotenv()
# Get passphrase
passphrase = get_passphrase(PassphraseSource.KEYRING)
# Decrypt specific values
raw_password = os.environ["DB_PASSWORD"]
if raw_password.startswith("ENC[v1]:"):
db_password = unseal(raw_password, passphrase).decode()
else:
db_password = raw_password
File Processing
The seal-file command processes environment files in key=value format and provides two modes:
Standard Mode (Default)
Encrypts all unencrypted values in the file:
# Before
DATABASE_URL=postgresql://user:secret@localhost/db
API_KEY=abc123
ALREADY_SEALED=ENC[v1]:eyJzIjoiNnZ...
# After running: envseal seal-file .env
DATABASE_URL=ENC[v1]:eyJzIjoiOXR...
API_KEY=ENC[v1]:eyJzIjoiNnZ...
ALREADY_SEALED=ENC[v1]:eyJzIjoiNnZ... # Unchanged (already encrypted)
Prefix-Only Mode (Selective Encryption)
Only encrypts values that are marked with the ENC[v1]: prefix. This allows you to manually control which values should be encrypted:
# Before - manually mark values you want encrypted
DATABASE_URL=postgresql://user:secret@localhost/db
API_KEY=abc123
DB_PASSWORD=ENC[v1]:my-secret-password
PORT=ENC[v1]:5432
# After running: envseal seal-file .env --prefix-only
DATABASE_URL=postgresql://user:secret@localhost/db # Unchanged (no prefix)
API_KEY=abc123 # Unchanged (no prefix)
DB_PASSWORD=ENC[v1]:eyJzIjoiOXR... # Encrypted (had prefix marker)
PORT=ENC[v1]:eyJzIjoiNnZ... # Encrypted (had prefix marker)
Workflow for selective encryption:
-
Edit your .env file and add
ENC[v1]:prefix to values you want encrypted:# Public/non-sensitive values - no prefix APP_NAME=myapp LOG_LEVEL=info # Sensitive values - add ENC[v1]: prefix as marker DB_PASSWORD=ENC[v1]:my-secret-password API_KEY=ENC[v1]:sk-1234567890abcdef
-
Run
envseal seal-file .env --prefix-onlyto encrypt only the marked values -
The file will now contain properly encrypted values:
APP_NAME=myapp LOG_LEVEL=info DB_PASSWORD=ENC[v1]:eyJzIjoiNnZ4dWFsOG... API_KEY=ENC[v1]:eyJzIjoiOXR2Y2FsOG...
This approach is perfect when you have mixed environment files with both sensitive and non-sensitive values, and you want precise control over what gets encrypted.
File Processing Features
- Preserves formatting: Maintains indentation, comments, and empty lines
- Handles quotes: Preserves single and double quotes around values
- Backup support: Create backups before modification with
--backup - Output control: Write to different file with
--output - Error handling: Detailed error messages for malformed files or encryption failures
- Smart detection: Automatically detects already-encrypted values to avoid double-encryption
Passphrase Management
EnvSeal supports multiple ways to provide the master passphrase:
1. OS Keyring (Recommended)
from envseal import store_passphrase_in_keyring
# Store once
store_passphrase_in_keyring("your-master-passphrase")
# Use automatically
passphrase = get_passphrase(PassphraseSource.KEYRING)
2. Environment Variables
export ENVSEAL_PASSPHRASE="your-master-passphrase"
passphrase = get_passphrase(PassphraseSource.ENV_VAR)
3. .env Files
Create a separate .env file for the passphrase:
# .passphrase.env
ENVSEAL_PASSPHRASE=your-master-passphrase
passphrase = get_passphrase(
PassphraseSource.DOTENV,
dotenv_path=".passphrase.env"
)
4. Hardcoded (Development Only)
passphrase = get_passphrase(
PassphraseSource.HARDCODED,
hardcoded_passphrase="dev-passphrase"
)
Security Best Practices
- Use OS Keyring: Store your master passphrase in the OS keyring for maximum security
- Separate Passphrase Storage: Never store the passphrase in the same file as encrypted values
- Environment-Specific Keys: Use different passphrases for different environments
- Regular Rotation: Rotate your master passphrase periodically
- Access Control: Limit access to systems that can decrypt your values
- Backup Strategy: Always backup your files before bulk encryption operations
- Selective Encryption: Use
--prefix-onlymode to encrypt only sensitive values in mixed files
Use Cases
Initial Setup
# 1. Store your master passphrase
envseal store-passphrase "your-secure-passphrase"
# 2. Encrypt your existing .env file
envseal seal-file .env --backup
# 3. Your secrets are now encrypted!
Selective Encryption for Mixed Files
# 1. Edit your .env file and mark sensitive values
# Change: DB_PASSWORD=secret123
# To: DB_PASSWORD=ENC[v1]:secret123
# 2. Encrypt only the marked values
envseal seal-file .env --prefix-only --backup
# 3. Only marked values are encrypted, others remain plain
Key Rotation
# 1. Update your passphrase in keyring
envseal store-passphrase "new-secure-passphrase"
# 2. Re-encrypt all encrypted values with new passphrase
envseal seal-file .env --backup
# 3. All encrypted values now use the new passphrase
API Reference
Core Functions
seal(plaintext, passphrase) -> str
Encrypt plaintext using AES-GCM.
unseal(token, passphrase) -> bytes
Decrypt an encrypted token.
seal_file(file_path, passphrase, output_path=None, prefix_only=False) -> int
Encrypt values in an environment file. Returns the number of values encrypted.
prefix_only=True: Only encrypt values marked withENC[v1]:prefixprefix_only=False: Encrypt all unencrypted values
get_passphrase(source, **kwargs) -> bytes
Get passphrase from various sources.
load_sealed_env(dotenv_path, passphrase_source, **kwargs) -> dict
Load environment variables with automatic decryption.
apply_sealed_env(dotenv_path, passphrase_source, override=False, **kwargs)
Load and apply environment variables to os.environ.
PassphraseSource Enum
KEYRING: OS keyringENV_VAR: Environment variableDOTENV: .env fileHARDCODED: Hardcoded stringPROMPT: Interactive prompt
Development
# Clone repository
git clone https://github.com/justtil/envseal.git
cd envseal
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
# Format code
black src/ tests/
isort src/ tests/
# Type checking
mypy src/
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Run the test suite
- Submit a pull request
Changelog
0.2.0
- Added
seal-filecommand for bulk file encryption - Added
--prefix-onlymode for selective encryption of marked values - Added backup and output file options
- Enhanced file processing with quote and formatting preservation
0.1.0
- Initial release
- AES-GCM encryption
- Multiple passphrase sources
- CLI and library interfaces
- python-dotenv integration
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file envseal-0.2.0.tar.gz.
File metadata
- Download URL: envseal-0.2.0.tar.gz
- Upload date:
- Size: 42.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82b6342a28950b81917412f127b979b67e1e7dfd5d000cb2b456ef3dd8a93eb3
|
|
| MD5 |
ce092d9ec339836acdc4af295c3a9ce9
|
|
| BLAKE2b-256 |
f63ce2d824f6fb00a29c810242d1235ae21ae7e339f991bc415d9270a1ce5054
|
File details
Details for the file envseal-0.2.0-py3-none-any.whl.
File metadata
- Download URL: envseal-0.2.0-py3-none-any.whl
- Upload date:
- Size: 14.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e1b3ff610cdebc74ca24877f26c4d79abfe9805152749fa002821efa38ca6ce
|
|
| MD5 |
218307fc63b99b00971bb2290b0f28a7
|
|
| BLAKE2b-256 |
b808726333303f45ce0c5ca7943b0b5bc2338215987db6db207f2a72ebf91b0a
|