SHA256
207 lines
6.8 KiB
Markdown
207 lines
6.8 KiB
Markdown
# Password Protection for markmywords
|
|
|
|
This document describes the password protection feature for encrypted markdown files in markmywords.
|
|
|
|
## Overview
|
|
|
|
When you encrypt a file with a password, it is automatically password-protected. Users must authenticate with the password before viewing the content in a browser.
|
|
|
|
**Important**: Only encrypted files (`.md.enc`) support password protection. Regular markdown files (`.md`) are always publicly viewable.
|
|
|
|
## Security Features
|
|
|
|
- **Bcrypt Hashing**: Passwords are hashed using bcrypt, a secure password hashing algorithm
|
|
- **Session Management**: Once authenticated, users remain authenticated for that file in their session
|
|
- **File Permissions**: Password database is stored with restricted file permissions (0600)
|
|
- **No Plain Text**: Passwords are never stored in plain text
|
|
- **HTTPS Ready**: Works seamlessly with HTTPS/SSL
|
|
- **Encryption Compatible**: Works with encrypted files for defense-in-depth security
|
|
|
|
## Configuration
|
|
|
|
### Environment Variables
|
|
|
|
```bash
|
|
# Set a strong secret key for Flask sessions (REQUIRED for production)
|
|
export FLASK_SECRET_KEY="your-very-secure-random-key"
|
|
|
|
# Optional: Set master admin password for programmatic file operations
|
|
export MARKMYWORDS_ADMIN_PASSWORD="your-admin-password"
|
|
```
|
|
|
|
## Using the CLI Tool
|
|
|
|
The `manage_content.py` script provides encryption and password protection management.
|
|
|
|
### Encrypt a File (Automatically Protected)
|
|
|
|
```bash
|
|
python manage_content.py encrypt <filepath> <password>
|
|
|
|
# Example:
|
|
python manage_content.py encrypt content/docs/secret-guide.md "mypassword123"
|
|
```
|
|
|
|
This encrypts the file and automatically sets password protection. The same password is used for both encryption and browser authentication.
|
|
|
|
### Remove Password Requirement (Keep Encryption)
|
|
|
|
```bash
|
|
python manage_content.py unprotect <filepath>
|
|
|
|
# Example:
|
|
python manage_content.py unprotect content/docs/secret-guide.md.enc
|
|
|
|
# File stays encrypted but no password prompt in browser
|
|
```
|
|
|
|
### List Protected Files
|
|
|
|
```bash
|
|
# List all encrypted/protected files
|
|
python manage_content.py list
|
|
|
|
# List encrypted files in a directory
|
|
python manage_content.py list content/docs/
|
|
```
|
|
|
|
### Check Protection Status
|
|
|
|
```bash
|
|
python manage_content.py status <filepath>
|
|
|
|
# Example:
|
|
python manage_content.py status content/docs/secret-guide.md.enc
|
|
|
|
# Output shows: Encrypted: Yes, Protected: Yes
|
|
```
|
|
|
|
## Encrypted Files with Automatic Protection
|
|
|
|
When you encrypt a file, password protection is automatic:
|
|
|
|
```bash
|
|
# Encrypt a file - automatically sets password protection
|
|
python manage_content.py encrypt content/secret.md "mypassword"
|
|
|
|
# In browser:
|
|
# 1. User navigates to file
|
|
# 2. User sees password prompt (same password as encryption)
|
|
# 3. User enters password
|
|
# 4. File is decrypted and displayed with full markdown support
|
|
```
|
|
|
|
The same password is used for both encryption and browser authentication.
|
|
|
|
## Using the API
|
|
|
|
### Authenticate for an Encrypted File
|
|
|
|
```bash
|
|
curl -X POST http://localhost:5000/api/auth/docs/secret-guide.md.enc \
|
|
-d "password=mypassword123"
|
|
|
|
# Response:
|
|
# {"success": true, "redirect": "/view/docs/secret-guide.md.enc"}
|
|
```
|
|
|
|
After successful authentication, the password is stored in the session and used to decrypt the file for display.
|
|
|
|
## Password Database
|
|
|
|
Passwords are stored in `/config/page_passwords.json` with the following structure:
|
|
|
|
```json
|
|
{
|
|
"content/path/to/file.md": {
|
|
"protected": true,
|
|
"password_hash": "$2b$12$...",
|
|
"created_at": "2024-01-15T10:30:00.000000"
|
|
},
|
|
"content/path/to/encrypted.md.enc": {
|
|
"encrypted": true,
|
|
"protected": true,
|
|
"password_hash": "$2b$12$...",
|
|
"encrypted_at": "2024-01-15T10:30:00.000000",
|
|
"created_at": "2024-01-15T10:30:00.000000"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Important**: This file should be backed up and kept secure. The password hashes cannot be reversed, but the file itself should have restricted access.
|
|
|
|
## How It Works
|
|
|
|
1. **User Requests File**: User navigates to a protected file
|
|
2. **Protection Check**: Application checks if file is password protected
|
|
3. **Password Prompt**: If protected and user not authenticated, show password form
|
|
4. **Authentication**: User enters password
|
|
5. **Verification**: Password is checked against the stored bcrypt hash
|
|
6. **Session Storage**: On success, file authentication is stored in user's session
|
|
7. **Content Display**: Protected file content is displayed
|
|
8. **Session Expiry**: Authentication expires when user's session expires (typically when browser is closed)
|
|
|
|
## Use Cases
|
|
|
|
- **Sensitive Documentation**: Encrypt internal documentation
|
|
- **Private Notes**: Keep personal notes encrypted and password-protected
|
|
- **Confidential Information**: Encrypt security guidelines, API keys, or business secrets
|
|
- **Draft Content**: Encrypt unfinished or embargoed content with password protection
|
|
- **Temporary Public Access**: Use `unprotect` to share encrypted content without password requirement
|
|
|
|
## Best Practices
|
|
|
|
1. **Strong Passwords**: Use passwords with at least 8 characters including mixed case and numbers
|
|
2. **Unique Passwords**: Use different passwords for different files
|
|
3. **Regular Backups**: Backup the password database
|
|
4. **Secure Secret Key**: Set a strong `FLASK_SECRET_KEY` environment variable
|
|
5. **HTTPS**: Always use HTTPS in production
|
|
6. **Share Carefully**: Share passwords through secure channels only
|
|
7. **Monitor Access**: Check logs for authentication attempts
|
|
|
|
## Troubleshooting
|
|
|
|
### "Invalid password" error repeatedly
|
|
|
|
- Check that the password is exactly correct (case-sensitive)
|
|
- Verify the file is actually protected: `python manage_content.py status content/path/to/file.md`
|
|
|
|
### Session expires too quickly
|
|
|
|
- The session expires based on Flask's session cookie settings
|
|
- For persistent authentication longer than the browser session, consider implementing "Remember Me" functionality
|
|
|
|
### Lost access to protected file
|
|
|
|
- If you lose the password, you must unprotect the file using the CLI:
|
|
```bash
|
|
python manage_content.py unprotect content/path/to/file.md
|
|
```
|
|
- Then set a new password if desired
|
|
|
|
### Password database corruption
|
|
|
|
- If `/config/page_passwords.json` becomes corrupted, delete it and recreate protections:
|
|
```bash
|
|
rm /config/page_passwords.json
|
|
python manage_content.py protect content/path/to/file.md <password>
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
- **Never** share passwords via email or unencrypted channels
|
|
- **Always** use HTTPS in production
|
|
- **Regularly** audit which files are protected
|
|
- **Securely** delete the password database when no longer needed
|
|
- **Use** strong, random passwords generated by a password manager
|
|
- **Backup** the password database in a secure location
|
|
|
|
## Future Enhancements
|
|
|
|
Potential future features:
|
|
- Per-user authentication and roles
|
|
- IP whitelisting for protected files
|
|
- Audit logging of access attempts
|
|
- Time-limited access links
|
|
- Integration with external authentication systems (LDAP, OAuth, SAML)
|