From 68ff4238e0c1fbe129d4583e6594bb6cd8c31bdb Mon Sep 17 00:00:00 2001 From: k3nny Date: Sun, 5 Oct 2025 01:29:16 +0200 Subject: [PATCH] fix(security): remove hardcoded Flask secret key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded Flask secret key with environment variable to prevent session hijacking and CSRF attacks. Changes: - Load secret key from TISBACKUP_SECRET_KEY environment variable - Fall back to cryptographically secure random key using secrets module - Log warning when random key is used (sessions won't persist) - Add environment variable example to README.md Docker Compose config - Add setup instructions in Configuration section Security improvements: - Eliminates hardcoded secret in source code - Uses secrets.token_hex(32) for cryptographically strong random generation - Sessions remain secure even without env var (though won't persist) - Prevents session hijacking and CSRF bypass attacks Documentation: - Update README.md with TISBACKUP_SECRET_KEY setup instructions - Include command to generate secure random key - Update SECURITY_IMPROVEMENTS.md with implementation details - Mark hardcoded secret key issue as resolved Setup: ```bash # Generate secure key python3 -c "import secrets; print(secrets.token_hex(32))" # Set in environment export TISBACKUP_SECRET_KEY=your-key-here ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 10 ++++++++ SECURITY_IMPROVEMENTS.md | 51 +++++++++++++++++++++++++++++++++++----- tisbackup_gui.py | 16 ++++++++++++- 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index febc664..4f989f7 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,10 @@ services: - ./backup/:/backup/ - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro + environment: + # SECURITY: Set a unique secret key for Flask session security + # Generate with: python3 -c "import secrets; print(secrets.token_hex(32))" + - TISBACKUP_SECRET_KEY=your-secret-key-here-change-me restart: unless-stopped ports: - 9980:8080 @@ -70,6 +74,12 @@ services: * Provide an SSH key and store it in `./ssh` * Setup config files in the `./config` directory + * **SECURITY**: Generate and set a secure Flask secret key: + ```bash + # Generate a secure random secret key + python3 -c "import secrets; print(secrets.token_hex(32))" + ``` + Then add it to your `compose.yml` as the `TISBACKUP_SECRET_KEY` environment variable **tisbackup-config.ini** diff --git a/SECURITY_IMPROVEMENTS.md b/SECURITY_IMPROVEMENTS.md index db992d5..78fadd3 100644 --- a/SECURITY_IMPROVEMENTS.md +++ b/SECURITY_IMPROVEMENTS.md @@ -104,26 +104,65 @@ import subprocess **Impact:** Cleaner namespace, easier to track dependencies -## Remaining Security Issues (Critical - Not Fixed) +### 7. Fixed Hardcoded Secret Key +**Files Modified:** [tisbackup_gui.py:67-79](tisbackup_gui.py#L67), [README.md](README.md) -### 1. **Hardcoded Secret Key** ([tisbackup_gui.py:64](tisbackup_gui.py#L64)) +**Before:** ```python app.secret_key = "fsiqefiuqsefARZ4Zfesfe34234dfzefzfe" ``` -**Recommendation:** Load from environment variable or secure config file -### 2. **No Authentication on Flask Routes** +**After:** +```python +SECRET_KEY = os.environ.get("TISBACKUP_SECRET_KEY") +if not SECRET_KEY: + import secrets + SECRET_KEY = secrets.token_hex(32) + logging.warning( + "TISBACKUP_SECRET_KEY environment variable not set. " + "Using a randomly generated secret key. " + "Sessions will not persist across application restarts. " + "Set TISBACKUP_SECRET_KEY environment variable for production use." + ) +app.secret_key = SECRET_KEY +``` + +**Changes:** +- Reads secret key from `TISBACKUP_SECRET_KEY` environment variable +- Falls back to cryptographically secure random key if not set +- Logs warning when using random key (sessions won't persist across restarts) +- Uses Python's `secrets` module for cryptographically strong random generation +- Updated README.md with setup instructions + +**Setup Instructions:** +```bash +# Generate a secure secret key +python3 -c "import secrets; print(secrets.token_hex(32))" + +# Set in Docker Compose (compose.yml) +environment: + - TISBACKUP_SECRET_KEY=your-generated-key-here + +# Or export in shell +export TISBACKUP_SECRET_KEY=your-generated-key-here +``` + +**Security Impact:** Eliminates hardcoded secret in source code, prevents session hijacking and CSRF attacks + +## Remaining Security Issues (Critical - Not Fixed) + +### 1. **No Authentication on Flask Routes** All routes are publicly accessible without authentication. **Recommendation:** Implement Flask-Login or similar authentication -### 3. **Insecure SSH Host Key Policy** ([libtisbackup/common.py:649](libtisbackup/common.py#L649)) +### 2. **Insecure SSH Host Key Policy** ([libtisbackup/common.py:649](libtisbackup/common.py#L649)) ```python ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ``` **Recommendation:** Use proper host key verification with known_hosts -### 4. **Command Injection in Legacy Code** +### 3. **Command Injection in Legacy Code** Multiple files still use `subprocess.call(shell_string, shell=True)` and `subprocess.Popen(..., shell=True)`: - [libtisbackup/common.py:128](libtisbackup/common.py#L128) - [libtisbackup/common.py:883](libtisbackup/common.py#L883) diff --git a/tisbackup_gui.py b/tisbackup_gui.py index 5ef295f..ac6dba0 100755 --- a/tisbackup_gui.py +++ b/tisbackup_gui.py @@ -62,7 +62,21 @@ mindate = None error = None info = None app = Flask(__name__) -app.secret_key = "fsiqefiuqsefARZ4Zfesfe34234dfzefzfe" + +# Load secret key from environment variable or generate a secure random one +SECRET_KEY = os.environ.get("TISBACKUP_SECRET_KEY") +if not SECRET_KEY: + # Generate a secure random secret key if not provided + import secrets + SECRET_KEY = secrets.token_hex(32) + # Warn if using a random key (sessions won't persist across restarts) + logging.warning( + "TISBACKUP_SECRET_KEY environment variable not set. Using a randomly generated secret key. " + "Sessions will not persist across application restarts. " + "Set TISBACKUP_SECRET_KEY environment variable for production use." + ) + +app.secret_key = SECRET_KEY app.config["PROPAGATE_EXCEPTIONS"] = True tasks_db = os.path.join(tisbackup_root_dir, "tasks.sqlite")