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:
parent
debc753f13
commit
68ff4238e0
10
README.md
10
README.md
@ -47,6 +47,10 @@ services:
|
|||||||
- ./backup/:/backup/
|
- ./backup/:/backup/
|
||||||
- /etc/timezone:/etc/timezone:ro
|
- /etc/timezone:/etc/timezone:ro
|
||||||
- /etc/localtime:/etc/localtime: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
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 9980:8080
|
- 9980:8080
|
||||||
@ -70,6 +74,12 @@ services:
|
|||||||
|
|
||||||
* Provide an SSH key and store it in `./ssh`
|
* Provide an SSH key and store it in `./ssh`
|
||||||
* Setup config files in the `./config` directory
|
* 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**
|
**tisbackup-config.ini**
|
||||||
|
|
||||||
|
@ -104,26 +104,65 @@ import subprocess
|
|||||||
|
|
||||||
**Impact:** Cleaner namespace, easier to track dependencies
|
**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
|
```python
|
||||||
app.secret_key = "fsiqefiuqsefARZ4Zfesfe34234dfzefzfe"
|
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.
|
All routes are publicly accessible without authentication.
|
||||||
|
|
||||||
**Recommendation:** Implement Flask-Login or similar 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
|
```python
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
```
|
```
|
||||||
**Recommendation:** Use proper host key verification with known_hosts
|
**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)`:
|
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:128](libtisbackup/common.py#L128)
|
||||||
- [libtisbackup/common.py:883](libtisbackup/common.py#L883)
|
- [libtisbackup/common.py:883](libtisbackup/common.py#L883)
|
||||||
|
@ -62,7 +62,21 @@ mindate = None
|
|||||||
error = None
|
error = None
|
||||||
info = None
|
info = None
|
||||||
app = Flask(__name__)
|
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
|
app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||||
|
|
||||||
tasks_db = os.path.join(tisbackup_root_dir, "tasks.sqlite")
|
tasks_db = os.path.join(tisbackup_root_dir, "tasks.sqlite")
|
||||||
|
Loading…
Reference in New Issue
Block a user