416
docs/archive/SECURITY_AUDIT_2025-10-31.md
Normal file
416
docs/archive/SECURITY_AUDIT_2025-10-31.md
Normal file
@@ -0,0 +1,416 @@
|
||||
# Security Audit Report
|
||||
**Date:** 2025-10-31
|
||||
**Application:** Media Downloader v6.3.3
|
||||
**Auditor:** Claude Code
|
||||
**Severity Levels:** 🔴 Critical | 🟠 High | 🟡 Medium | 🟢 Low
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
A comprehensive security audit was conducted on the Media Downloader application. **6 critical vulnerabilities** were identified that require immediate attention. The application has good foundations (bcrypt, JWT, rate limiting) but lacks proper authentication enforcement and network security.
|
||||
|
||||
**Risk Level:** 🔴 **CRITICAL**
|
||||
|
||||
---
|
||||
|
||||
## Critical Vulnerabilities (Immediate Action Required)
|
||||
|
||||
### 🔴 1. NO FIREWALL ENABLED
|
||||
**Severity:** CRITICAL
|
||||
**Impact:** All services exposed to network
|
||||
|
||||
**Finding:**
|
||||
```bash
|
||||
$ sudo ufw status
|
||||
Status: inactive
|
||||
```
|
||||
|
||||
**Exposed Services:**
|
||||
- Port 8000: FastAPI backend (0.0.0.0 - all interfaces)
|
||||
- Port 5173: Vite dev server (0.0.0.0 - all interfaces)
|
||||
- Port 3456: Node service (0.0.0.0 - all interfaces)
|
||||
- Port 80: Nginx
|
||||
|
||||
**Risk:**
|
||||
- Anyone on your network (192.168.1.0/24) can access these services
|
||||
- If port-forwarded, services are exposed to the entire internet
|
||||
- No protection against port scans or automated attacks
|
||||
|
||||
**Fix (URGENT - 15 minutes):**
|
||||
```bash
|
||||
# Enable firewall
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
|
||||
# Allow SSH (if remote)
|
||||
sudo ufw allow 22/tcp
|
||||
|
||||
# Allow only nginx (reverse proxy)
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
|
||||
# Block direct access to backend ports
|
||||
# (nginx should proxy to localhost:8000)
|
||||
|
||||
# Enable firewall
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔴 2. 95% OF API ENDPOINTS ARE UNAUTHENTICATED
|
||||
**Severity:** CRITICAL
|
||||
**Impact:** Anyone can access/modify your data
|
||||
|
||||
**Finding:**
|
||||
- Total endpoints: 43
|
||||
- Authenticated: 2 (4.6%)
|
||||
- **Public (no auth): 41 (95.4%)**
|
||||
|
||||
**Unauthenticated Endpoints Include:**
|
||||
- `/api/downloads` - View ALL downloads
|
||||
- `/api/downloads/{id}` - DELETE downloads
|
||||
- `/api/platforms/{platform}/trigger` - Trigger downloads
|
||||
- `/api/scheduler/current-activity/stop` - Stop downloads
|
||||
- `/api/scheduler/tasks/{task_id}/skip` - Modify schedule
|
||||
- `/api/config` - View/modify configuration
|
||||
- `/api/media/*` - Access all media files
|
||||
|
||||
**Risk:**
|
||||
- Anyone on your network can:
|
||||
- View all your downloads
|
||||
- Delete your files
|
||||
- Trigger new downloads
|
||||
- Stop running downloads
|
||||
- Modify configuration
|
||||
- Access your media library
|
||||
|
||||
**Fix (HIGH PRIORITY - 2 hours):**
|
||||
Add `Depends(get_current_user)` to all sensitive endpoints:
|
||||
|
||||
```python
|
||||
# BEFORE (VULNERABLE)
|
||||
@app.delete("/api/downloads/{download_id}")
|
||||
async def delete_download(download_id: int):
|
||||
|
||||
# AFTER (SECURE)
|
||||
@app.delete("/api/downloads/{download_id}")
|
||||
async def delete_download(
|
||||
download_id: int,
|
||||
current_user: Dict = Depends(get_current_user) # ADD THIS
|
||||
):
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔴 3. DATABASES ARE WORLD-READABLE
|
||||
**Severity:** CRITICAL
|
||||
**Impact:** Sensitive data exposure
|
||||
|
||||
**Finding:**
|
||||
```bash
|
||||
-rw-r--r-- root root /opt/media-downloader/database/auth.db
|
||||
-rw-r--r-- root root /opt/media-downloader/database/media_downloader.db
|
||||
```
|
||||
|
||||
**Risk:**
|
||||
- Any user on the system can read:
|
||||
- Password hashes (auth.db)
|
||||
- User sessions and tokens
|
||||
- Download history
|
||||
- All metadata
|
||||
|
||||
**Fix (5 minutes):**
|
||||
```bash
|
||||
# Restrict database permissions
|
||||
sudo chmod 600 /opt/media-downloader/database/*.db
|
||||
sudo chown root:root /opt/media-downloader/database/*.db
|
||||
|
||||
# Verify
|
||||
ls -la /opt/media-downloader/database/*.db
|
||||
# Should show: -rw------- root root
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔴 4. DEVELOPMENT SERVERS RUNNING IN PRODUCTION
|
||||
**Severity:** HIGH
|
||||
**Impact:** Performance, stability, security
|
||||
|
||||
**Finding:**
|
||||
- Vite dev server on port 5173 (should be built static files)
|
||||
- Development mode has verbose errors, source maps, hot reload
|
||||
- Not optimized for production
|
||||
|
||||
**Risk:**
|
||||
- Exposes source code and stack traces
|
||||
- Poor performance
|
||||
- Memory leaks
|
||||
- Not designed for production load
|
||||
|
||||
**Fix (30 minutes):**
|
||||
```bash
|
||||
# Build production frontend
|
||||
cd /opt/media-downloader/web/frontend
|
||||
npm run build
|
||||
|
||||
# Serve via nginx, not Vite dev server
|
||||
# Update nginx config to serve dist/ folder
|
||||
|
||||
# Stop Vite dev server
|
||||
sudo systemctl stop vite-dev-server # (if running as service)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔴 5. NO RATE LIMITING ON API
|
||||
**Severity:** HIGH
|
||||
**Impact:** Denial of Service, brute force attacks
|
||||
|
||||
**Finding:**
|
||||
- No rate limiting middleware on FastAPI
|
||||
- Login endpoint has application-level rate limiting (good)
|
||||
- But other endpoints have no protection
|
||||
|
||||
**Risk:**
|
||||
- API can be flooded with requests
|
||||
- Download all your files via API spam
|
||||
- Trigger hundreds of downloads simultaneously
|
||||
- DDoS the service
|
||||
|
||||
**Fix (2 hours):**
|
||||
Install slowapi:
|
||||
```python
|
||||
from slowapi import Limiter, _rate_limit_exceeded_handler
|
||||
from slowapi.util import get_remote_address
|
||||
from slowapi.errors import RateLimitExceeded
|
||||
|
||||
limiter = Limiter(key_func=get_remote_address)
|
||||
app.state.limiter = limiter
|
||||
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
||||
|
||||
# Apply to routes
|
||||
@app.get("/api/downloads")
|
||||
@limiter.limit("10/minute") # 10 requests per minute
|
||||
async def get_downloads(...):
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🟠 6. MIXED COOKIE FILE PERMISSIONS
|
||||
**Severity:** MEDIUM
|
||||
**Impact:** Session hijacking potential
|
||||
|
||||
**Finding:**
|
||||
```bash
|
||||
-rw-r--r-- 1 root root 1140 fastdl_cookies.json # World-readable
|
||||
-rw------- 1 root root 902 forum_cookies.json # Secure
|
||||
-rw-rw-r-- 1 root root 4084 toolzu_cookies.json # Group-writable
|
||||
```
|
||||
|
||||
**Risk:**
|
||||
- Other users/processes can steal cookies
|
||||
- Session hijacking across platforms
|
||||
|
||||
**Fix (2 minutes):**
|
||||
```bash
|
||||
sudo chmod 600 /opt/media-downloader/cookies/*.json
|
||||
sudo chown root:root /opt/media-downloader/cookies/*.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Security Concerns
|
||||
|
||||
### 🟡 7. CORS Configuration (Development Only)
|
||||
**Current:**
|
||||
```python
|
||||
allow_origins=["http://localhost:5173", "http://localhost:3000"]
|
||||
```
|
||||
|
||||
**Issue:** If accessed via IP or domain name, CORS will block. Need production config.
|
||||
|
||||
**Fix:**
|
||||
```python
|
||||
# Production
|
||||
allow_origins=["https://yourdomain.com"]
|
||||
|
||||
# Or if same-origin (nginx proxy)
|
||||
# No CORS needed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🟡 8. JWT Secret Key
|
||||
**Current:**
|
||||
```python
|
||||
SECRET_KEY = os.environ.get("JWT_SECRET_KEY", secrets.token_urlsafe(32))
|
||||
```
|
||||
|
||||
**Issue:**
|
||||
- Falls back to random key on each restart
|
||||
- Invalidates all sessions on restart
|
||||
- Not persisted
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
# Generate and save secret
|
||||
echo "JWT_SECRET_KEY=$(openssl rand -hex 32)" | sudo tee -a /etc/environment
|
||||
|
||||
# Restart services to pick up env var
|
||||
sudo systemctl restart media-downloader-api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🟡 9. No HTTPS/TLS
|
||||
**Finding:** Services run on HTTP only
|
||||
|
||||
**Risk:**
|
||||
- Passwords transmitted in clear text
|
||||
- Session tokens visible on network
|
||||
- Man-in-the-middle attacks
|
||||
|
||||
**Fix:**
|
||||
Use Let's Encrypt with Certbot:
|
||||
```bash
|
||||
sudo certbot --nginx -d yourdomain.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🟢 10. Log Files Growing Unbounded
|
||||
**Finding:**
|
||||
- service.log: 15MB
|
||||
- web-api.log: 2.3MB
|
||||
- No rotation configured
|
||||
|
||||
**Risk:** Disk space exhaustion
|
||||
|
||||
**Fix:** Already recommended in previous report (logrotate)
|
||||
|
||||
---
|
||||
|
||||
## What's Secure (Good Practices Found)
|
||||
|
||||
✅ **Password Hashing:** Using bcrypt (industry standard)
|
||||
✅ **JWT Implementation:** Using jose library correctly
|
||||
✅ **Login Rate Limiting:** 5 attempts, 15 min lockout
|
||||
✅ **SQL Injection:** No f-string queries, using parameterized queries
|
||||
✅ **Session Management:** Proper session table with expiration
|
||||
✅ **CORS (Dev):** Restricted to localhost during development
|
||||
|
||||
---
|
||||
|
||||
## Recommended Action Plan
|
||||
|
||||
### Phase 1: IMMEDIATE (Do NOW - 1 hour total)
|
||||
|
||||
**Priority 1:** Enable Firewall (15 min)
|
||||
```bash
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
sudo ufw allow 22/tcp # SSH
|
||||
sudo ufw allow 80/tcp # HTTP
|
||||
sudo ufw allow 443/tcp # HTTPS
|
||||
sudo ufw enable
|
||||
sudo ufw status
|
||||
```
|
||||
|
||||
**Priority 2:** Fix Database Permissions (5 min)
|
||||
```bash
|
||||
sudo chmod 600 /opt/media-downloader/database/*.db
|
||||
sudo chmod 600 /opt/media-downloader/cookies/*.json
|
||||
```
|
||||
|
||||
**Priority 3:** Set JWT Secret (5 min)
|
||||
```bash
|
||||
openssl rand -hex 32 | sudo tee /opt/media-downloader/.jwt_secret
|
||||
echo "JWT_SECRET_KEY=$(cat /opt/media-downloader/.jwt_secret)" | sudo tee -a /etc/environment
|
||||
sudo chmod 600 /opt/media-downloader/.jwt_secret
|
||||
sudo systemctl restart media-downloader-api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: URGENT (Do Today - 2-3 hours)
|
||||
|
||||
**Priority 4:** Add Authentication to API Endpoints (2 hours)
|
||||
|
||||
Create a comprehensive list of endpoints that need auth:
|
||||
- All DELETE operations
|
||||
- All POST operations (except /api/auth/login)
|
||||
- All configuration endpoints
|
||||
- All download/media access endpoints
|
||||
|
||||
**Priority 5:** Add Rate Limiting (1 hour)
|
||||
|
||||
Install and configure slowapi on all endpoints.
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: IMPORTANT (Do This Week)
|
||||
|
||||
**Priority 6:** Production Frontend Build
|
||||
- Stop Vite dev server
|
||||
- Configure nginx to serve static build
|
||||
- Remove development dependencies
|
||||
|
||||
**Priority 7:** HTTPS Setup
|
||||
- Obtain SSL certificate
|
||||
- Configure nginx for HTTPS
|
||||
- Redirect HTTP to HTTPS
|
||||
|
||||
**Priority 8:** Network Segmentation
|
||||
- Consider running services on localhost only
|
||||
- Use nginx as reverse proxy
|
||||
- Only expose nginx to network
|
||||
|
||||
---
|
||||
|
||||
## Security Best Practices for Future
|
||||
|
||||
1. **Always require authentication** - Default deny, explicitly allow
|
||||
2. **Principle of least privilege** - Restrict file permissions
|
||||
3. **Defense in depth** - Firewall + authentication + rate limiting
|
||||
4. **Regular security audits** - Review code and config quarterly
|
||||
5. **Keep dependencies updated** - Run `npm audit` and `pip audit`
|
||||
6. **Monitor logs** - Watch for suspicious activity
|
||||
7. **Backup encryption keys** - Store JWT secret securely
|
||||
|
||||
---
|
||||
|
||||
## Testing Your Security
|
||||
|
||||
After implementing fixes, verify:
|
||||
|
||||
```bash
|
||||
# 1. Firewall is active
|
||||
sudo ufw status
|
||||
|
||||
# 2. Services not directly accessible
|
||||
curl http://192.168.1.6:8000/api/downloads
|
||||
# Should fail or require auth
|
||||
|
||||
# 3. File permissions correct
|
||||
ls -la /opt/media-downloader/database/
|
||||
# Should show -rw------- (600)
|
||||
|
||||
# 4. API requires auth
|
||||
curl -H "Content-Type: application/json" \
|
||||
http://localhost/api/downloads
|
||||
# Should return 401 Unauthorized
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Questions?
|
||||
|
||||
Review this document and implement Phase 1 (IMMEDIATE) fixes right away. The firewall and file permissions take less than 30 minutes total but dramatically improve security.
|
||||
|
||||
**Current Risk Level:** 🔴 CRITICAL
|
||||
**After Phase 1:** 🟠 HIGH
|
||||
**After Phase 2:** 🟡 MEDIUM
|
||||
**After Phase 3:** 🟢 LOW
|
||||
|
||||
Reference in New Issue
Block a user