# Encrypted Files & Password Protection This document describes how to encrypt and password-protect markdown files in markmywords. ## Overview markmywords supports file encryption with automatic password protection. When you encrypt a file: - It becomes password-protected automatically (same password for both encryption and browser access) - Users must authenticate in the browser before viewing - File is decrypted and displayed with full markdown support (code highlighting, tables, etc.) - File can also be decrypted from CLI for offline access or backups - Only encrypted files (`.md.enc`) support password protection; regular `.md` files are always public ## Encryption Details - **Algorithm**: Fernet (symmetric encryption based on AES-128) - **Key Derivation**: PBKDF2 with SHA256, 100,000 iterations - **Fixed Salt**: `markmywords_salt` (enables consistent key derivation across sessions) - **File Format**: Encrypted files use `.md.enc` extension ## Security Features - **Bcrypt Hashing**: Passwords are hashed using bcrypt for secure storage - **Session Management**: Once authenticated in browser, users remain authenticated during their session - **File Permissions**: Password database stored with restricted permissions (0600) - **No Plain Text**: Passwords never stored in plain text (only bcrypt hashes) - **HTTPS Ready**: Works seamlessly with HTTPS/SSL in production - **Same Password Model**: One password serves both encryption key derivation and browser authentication ## Configuration ### Environment Variables ```bash # REQUIRED for production - strong secret key for Flask sessions export FLASK_SECRET_KEY="your-very-secure-random-key" ``` These variables should be set in your Docker environment or `.env` file. ## Quick Start ### Encrypt a File ```bash # Encrypt a markdown file with a password # Automatically sets password protection - if encrypted, it requires a password python manage_content.py encrypt content/secret.md "mypassword" # This creates: content/secret.md.enc # The original file is deleted # The password is hashed and stored for browser access ``` ### Decrypt a File from CLI ```bash # Decrypt and view a file (prints to stdout) python manage_content.py decrypt content/secret.md.enc "mypassword" # Save to a new file python manage_content.py decrypt content/secret.md.enc "mypassword" > secret_decrypted.md ``` ### View Encrypted Files in Browser 1. Navigate to the file in the browser 2. You'll be prompted for the password (same password used for encryption) 3. Enter the password and the file will be decrypted and displayed with full markdown support ## Using the CLI Tool ### Encrypt a File ```bash python manage_content.py encrypt # Example: python manage_content.py encrypt content/docs/secret-guide.md "secure_password_123" ``` **Result**: - Original file is deleted - New encrypted file created: `content/docs/secret-guide.md.enc` - Password hashed and stored in config (required for browser access) - Decryption key is derived from the password - Both encryption and browser authentication use the same password - File is NOT viewable in browser without password ### Decrypt a File ```bash # Print decrypted content to stdout python manage_content.py decrypt # Example: python manage_content.py decrypt content/docs/secret-guide.md.enc "secure_password_123" # Save to a file python manage_content.py decrypt content/docs/secret-guide.md.enc "secure_password_123" > decrypted.md ``` ### Decrypt and Display as HTML ```bash # Show formatted markdown (requires markdown library) python manage_content.py view content/docs/secret-guide.md.enc "secure_password_123" ``` ### List Encrypted Files ```bash # List all encrypted files python manage_content.py list # List encrypted files in a directory python manage_content.py list content/docs/ ``` ### Check File Status ```bash # Check if a file is encrypted and protected python manage_content.py status content/secret.md.enc ``` ## Workflow: Encryption Always Requires Password When you encrypt a file, password protection is automatic: ```bash # Single command encrypts file AND sets password protection python manage_content.py encrypt content/secret.md "mypassword" # When viewing in browser: # - User sees password prompt (required) # - User enters "mypassword" # - Password is validated against stored hash # - File is decrypted and rendered as HTML ``` **Design principle**: If a file is encrypted, it must be password-protected. This ensures encrypted content is never readable without authentication. ## Optional: Remove Password Requirement If you want to make an encrypted file publicly viewable (while staying encrypted on disk): ```bash # Unprotect - removes password requirement but keeps encryption python manage_content.py unprotect content/secret.md.enc # Now the file is viewable in browser without password # But it's still encrypted in the filesystem # You can re-protect it later by encrypting again with new password ``` ## Security Considerations ### Password Requirements - Minimum 4 characters - Should be strong and unique for sensitive content - The same password is used for both encryption and key derivation ### File Access Control - Encrypted files require password authentication to view in browser - Encrypted content is never readable without the password - Password database is stored with restricted permissions (0600) - Session-based authentication persists only for the current browser session ### Key Derivation - Fixed salt is used for consistency (not random per file) - This allows decryption on any machine with the password - PBKDF2 with 100,000 iterations provides strong key derivation - Should be secure for typical use cases; for extremely sensitive data, consider additional measures ### Recommendations - Use HTTPS in production to prevent password interception - Use strong, unique passwords for sensitive content - Combine encryption + password protection for critical files - Regularly backup encrypted content with passwords stored securely - Consider using environment variables for passwords in automated scripts ## Programmatic Usage ### Python API ```python from app import encrypt_file_content, decrypt_file_content # Encrypt content password = "mypassword" original_content = "# My Secret Document\n\nThis is secret." encrypted = encrypt_file_content(original_content, password) # Save encrypted content with open("secret.md.enc", "w") as f: f.write(encrypted) # Decrypt content with open("secret.md.enc", "r") as f: encrypted_content = f.read() decrypted = decrypt_file_content(encrypted_content, password) print(decrypted) # "# My Secret Document\n\nThis is secret." ``` ### Key Derivation Details ```python from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2 from cryptography.hazmat.backends import default_backend import base64 def derive_encryption_key(password): salt = b'markmywords_salt' # Fixed salt kdf = PBKDF2( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, backend=default_backend() ) key = base64.urlsafe_b64encode(kdf.derive(password.encode())) return key # Same password always produces same key (due to fixed salt) key1 = derive_encryption_key("mypassword") key2 = derive_encryption_key("mypassword") assert key1 == key2 # True ``` ## Troubleshooting ### "Failed to decrypt" error - Verify the password is correct - Ensure the file is actually encrypted (has .md.enc extension) - Check that the file hasn't been corrupted ### Encrypted files not appearing in search - Encrypted files are excluded from the search index for security - They only appear when accessed directly with proper authentication - Once authenticated in browser, they display with full markdown support ### Browser shows incomplete markdown - Make sure authentication succeeded (file should have rendered) - Check browser console for errors - Verify the password was entered correctly ### Decryption works CLI but fails in browser - Browser authentication and decryption share the same password - Session may have expired; try re-authenticating - Check that file protection is set up correctly ## File Management Workflow ### Recommended Process 1. **Create and edit** markdown files in `content/` directory 2. **Test** locally: `python manage_content.py view content/file.md` 3. **Encrypt** when ready: `python manage_content.py encrypt content/file.md "password"` - Automatically sets password protection for browser access 4. **Deploy** via Docker: `docker-compose up -d` 5. **Access** in browser: File requires password to view; all markdown features work after authentication ### Backup Workflow ```bash # Export decrypted version for backup python manage_content.py decrypt content/secret.md.enc "password" > backup/secret_backup.md # Store password securely (not in version control) echo "password" > backup/secret_password.txt # Secure this file! chmod 600 backup/secret_password.txt ``` ## Environment Variables ```bash # Enable/disable encryption feature (default: true) export ENCRYPTION_ENABLED="true" ``` All encryption runs with the selected interpreter's cryptography library. Ensure `cryptography>=41.0.7` is installed.