Skip to main content

Encrypt sensitive values in environment files using AES-GCM

Project description

Logo and banner for the envseal python package

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:

  1. 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
    
  2. Run envseal seal-file .env --prefix-only to encrypt only the marked values

  3. 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

  1. Use OS Keyring: Store your master passphrase in the OS keyring for maximum security
  2. Separate Passphrase Storage: Never store the passphrase in the same file as encrypted values
  3. Environment-Specific Keys: Use different passphrases for different environments
  4. Regular Rotation: Rotate your master passphrase periodically
  5. Access Control: Limit access to systems that can decrypt your values
  6. Backup Strategy: Always backup your files before bulk encryption operations
  7. Selective Encryption: Use --prefix-only mode 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 with ENC[v1]: prefix
  • prefix_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 keyring
  • ENV_VAR: Environment variable
  • DOTENV: .env file
  • HARDCODED: Hardcoded string
  • PROMPT: 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

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Run the test suite
  6. Submit a pull request

Changelog

0.2.0

  • Added seal-file command for bulk file encryption
  • Added --prefix-only mode 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

envseal-0.2.0.tar.gz (42.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

envseal-0.2.0-py3-none-any.whl (14.7 kB view details)

Uploaded Python 3

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

Hashes for envseal-0.2.0.tar.gz
Algorithm Hash digest
SHA256 82b6342a28950b81917412f127b979b67e1e7dfd5d000cb2b456ef3dd8a93eb3
MD5 ce092d9ec339836acdc4af295c3a9ce9
BLAKE2b-256 f63ce2d824f6fb00a29c810242d1235ae21ae7e339f991bc415d9270a1ce5054

See more details on using hashes here.

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

Hashes for envseal-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e1b3ff610cdebc74ca24877f26c4d79abfe9805152749fa002821efa38ca6ce
MD5 218307fc63b99b00971bb2290b0f28a7
BLAKE2b-256 b808726333303f45ce0c5ca7943b0b5bc2338215987db6db207f2a72ebf91b0a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page