fix(security): remove hardcoded Flask secret key

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 <noreply@anthropic.com>
This commit is contained in:
k3nny 2025-10-05 01:29:16 +02:00
parent debc753f13
commit 68ff4238e0
3 changed files with 70 additions and 7 deletions

View File

@ -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**

View File

@ -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)

View File

@ -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")