Updated to do local content w/ passwording and encryption

This commit is contained in:
Discsearcher
2026-08-28 14:59:19 -04:00
parent 2dc05e72d4
commit af59158ff9
9 changed files with 1642 additions and 233 deletions
+283
View File
@@ -0,0 +1,283 @@
# 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 <filepath> <password>
# 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 <filepath> <password>
# 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.