diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 0000000..a4f9d9c --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,636 @@ +# Security Guide - Norda Biznes Hub + +**Last Updated:** 2026-01-10 +**Status:** Active +**Severity:** CRITICAL - Follow all guidelines + +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Database Credentials Management](#database-credentials-management) +3. [Environment Variables Reference](#environment-variables-reference) +4. [Development Environment Setup](#development-environment-setup) +5. [Production Environment Setup](#production-environment-setup) +6. [Shell Script Configuration](#shell-script-configuration) +7. [Security Best Practices](#security-best-practices) +8. [Verification and Testing](#verification-and-testing) +9. [Troubleshooting](#troubleshooting) +10. [Incident Response](#incident-response) + +--- + +## Overview + +This document provides comprehensive guidance on securely configuring database credentials and API keys for the Norda Biznes Hub platform. Following these guidelines is **mandatory** to prevent security vulnerabilities and protect sensitive data. + +### Security Vulnerability: CWE-798 + +**CWE-798: Use of Hard-coded Credentials** is a critical security vulnerability that occurs when passwords, API keys, or other sensitive credentials are embedded directly in source code. This practice: + +- ❌ Exposes credentials if repository becomes public +- ❌ Makes credential rotation extremely difficult +- ❌ Violates compliance frameworks (OWASP, PCI-DSS, SOC 2) +- ❌ Creates audit trail of credentials in Git history +- ❌ Allows unauthorized access if code is compromised + +**ALL credentials MUST be stored in environment variables, NEVER in source code.** + +--- + +## Database Credentials Management + +### Principles + +1. **Never commit credentials to Git** - Use `.env` files (already in `.gitignore`) +2. **Use environment variables** - All scripts read from `DATABASE_URL` or `PGPASSWORD` +3. **Safe fallback values** - Default values use `CHANGE_ME` placeholder (will fail fast) +4. **Fail fast** - Scripts exit with clear error if credentials missing +5. **Separate dev/prod** - Different credentials for each environment + +### Credential Storage Locations + +| Environment | Location | User | Access | +|-------------|----------|------|--------| +| **Development** | `.env` in project root | Developer | Local only | +| **Production** | `/var/www/nordabiznes/.env` | www-data | Server only | + +--- + +## Environment Variables Reference + +### Required Variables + +| Variable | Purpose | Used By | Example | +|----------|---------|---------|---------| +| `DATABASE_URL` | PostgreSQL connection string | Python scripts | `postgresql://user:pass@host:port/db` | +| `PGPASSWORD` | PostgreSQL password | Shell scripts (psql, pg_dump) | `your_secure_password` | +| `SECRET_KEY` | Flask session encryption | Flask app | Random string (64+ chars) | +| `GOOGLE_GEMINI_API_KEY` | Gemini AI chat | AI features | `AIzaSy...` | +| `GOOGLE_PAGESPEED_API_KEY` | SEO audits | Admin panel | `AIzaSy...` | +| `GOOGLE_PLACES_API_KEY` | Google Business Profile | GBP audits | `AIzaSy...` | +| `BRAVE_SEARCH_API_KEY` | News monitoring | News search | `BSA...` | + +### Optional Variables + +| Variable | Purpose | Default | Example | +|----------|---------|---------|---------| +| `FLASK_ENV` | Flask environment mode | `production` | `development` | +| `PORT` | Flask server port | `5000` | `5001` | +| `HOST` | Flask server host | `0.0.0.0` | `127.0.0.1` | +| `MAIL_SERVER` | Email SMTP server | None | `smtp.gmail.com` | +| `MAIL_USERNAME` | Email account | None | `noreply@example.com` | +| `MAIL_PASSWORD` | Email password/app password | None | `your_app_password` | + +--- + +## Development Environment Setup + +### Step 1: Create `.env` File + +```bash +# In project root directory +cp .env.example .env +``` + +### Step 2: Configure Database Credentials + +Edit `.env` and set your local database credentials: + +```bash +# Development PostgreSQL (Docker) +DATABASE_URL=postgresql://nordabiz_user:nordabiz_password@localhost:5433/nordabiz + +# Flask Configuration +SECRET_KEY=your-randomly-generated-secret-key-change-this +FLASK_ENV=development +PORT=5000 + +# API Keys (get from Google Cloud Console) +GOOGLE_GEMINI_API_KEY=your_gemini_key_here +GOOGLE_PAGESPEED_API_KEY=your_pagespeed_key_here +GOOGLE_PLACES_API_KEY=your_places_key_here +``` + +### Step 3: Start Local Database + +```bash +# Start PostgreSQL in Docker +docker compose up -d + +# Verify database is running +docker ps | grep postgres +``` + +### Step 4: Verify Configuration + +```bash +# Test Python scripts can read DATABASE_URL +python3 -c "import os; print('DATABASE_URL:', os.getenv('DATABASE_URL', 'NOT SET'))" + +# Expected output: +# DATABASE_URL: postgresql://nordabiz_user:nordabiz_password@localhost:5433/nordabiz +``` + +### Step 5: Run Application + +```bash +# Activate virtual environment +source venv/bin/activate + +# Run Flask application +python3 app.py +``` + +--- + +## Production Environment Setup + +### Step 1: SSH to Production Server + +```bash +ssh maciejpi@10.22.68.249 +``` + +**IMPORTANT:** Always SSH as `maciejpi`, NEVER as root! + +### Step 2: Configure Production `.env` + +```bash +# Navigate to application directory +cd /var/www/nordabiznes + +# Edit .env file (use sudo if needed) +sudo -u www-data nano .env +``` + +### Step 3: Set Production Credentials + +```bash +# Production PostgreSQL (same server) +DATABASE_URL=postgresql://nordabiz_app:YOUR_PRODUCTION_PASSWORD@127.0.0.1:5432/nordabiz + +# Flask Configuration +SECRET_KEY=your-production-secret-key-64-random-characters-minimum +FLASK_ENV=production +PORT=5000 +HOST=0.0.0.0 + +# API Keys (production keys from Google Cloud) +GOOGLE_GEMINI_API_KEY=your_production_gemini_key +GOOGLE_PAGESPEED_API_KEY=your_production_pagespeed_key +GOOGLE_PLACES_API_KEY=your_production_places_key +``` + +### Step 4: Set File Permissions + +```bash +# Ensure .env is readable only by www-data +sudo chown www-data:www-data /var/www/nordabiznes/.env +sudo chmod 600 /var/www/nordabiznes/.env + +# Verify permissions +ls -la /var/www/nordabiznes/.env +# Expected: -rw------- 1 www-data www-data +``` + +### Step 5: Restart Application + +```bash +sudo systemctl restart nordabiznes +sudo systemctl status nordabiznes +``` + +--- + +## Shell Script Configuration + +### Problem: Shell Scripts Cannot Read `.env` Files + +Shell scripts (like `view_maturity_results.sh`) **cannot automatically read** `.env` files. You must set `PGPASSWORD` manually in your shell session. + +### Solution 1: Export in Shell Session (Temporary) + +```bash +# Set PGPASSWORD for current shell session +export PGPASSWORD='your_database_password' + +# Run script +./view_maturity_results.sh + +# Unset after use (optional, for security) +unset PGPASSWORD +``` + +### Solution 2: Inline Environment Variable (One-time) + +```bash +# Set PGPASSWORD only for this command +PGPASSWORD='your_database_password' ./view_maturity_results.sh +``` + +### Solution 3: Use `.pgpass` File (Recommended for Production) + +The `.pgpass` file allows PostgreSQL tools to authenticate without environment variables. + +**Step 1: Create `.pgpass` file** + +```bash +# Create file in home directory +nano ~/.pgpass +``` + +**Step 2: Add credentials (one line per database)** + +``` +# Format: hostname:port:database:username:password +10.22.68.249:5432:nordabiz:nordabiz_app:your_production_password +localhost:5433:nordabiz:nordabiz_user:nordabiz_password +``` + +**Step 3: Set correct permissions (REQUIRED)** + +```bash +chmod 600 ~/.pgpass + +# Verify +ls -la ~/.pgpass +# Expected: -rw------- 1 your_user your_group +``` + +**Step 4: Run script (no PGPASSWORD needed)** + +```bash +./view_maturity_results.sh +# PostgreSQL will automatically read credentials from .pgpass +``` + +### Script Validation + +All shell scripts now validate that `PGPASSWORD` is set before execution: + +```bash +# Scripts check for PGPASSWORD at start +if [ -z "$PGPASSWORD" ]; then + echo "ERROR: PGPASSWORD environment variable is not set" + echo "Usage: PGPASSWORD='your_password' $0" + exit 1 +fi +``` + +**If you see this error**, set PGPASSWORD using one of the methods above. + +--- + +## Security Best Practices + +### 1. Never Hardcode Credentials + +**❌ WRONG - Hardcoded password in Python:** + +```python +# NEVER DO THIS +DATABASE_URL = 'postgresql://user:MyPassword123@localhost/db' +``` + +**✅ CORRECT - Environment variable with safe fallback:** + +```python +# Always use environment variables +DATABASE_URL = os.getenv('DATABASE_URL', 'postgresql://user:CHANGE_ME@localhost/db') +``` + +### 2. Never Hardcode Credentials in Shell Scripts + +**❌ WRONG - Hardcoded PGPASSWORD:** + +```bash +# NEVER DO THIS +PGPASSWORD='MyPassword123' psql -h localhost -U myuser -d mydb +``` + +**✅ CORRECT - Use environment variable:** + +```bash +# Validate variable is set +if [ -z "$PGPASSWORD" ]; then + echo "ERROR: PGPASSWORD not set" + exit 1 +fi + +# Use variable +psql -h localhost -U myuser -d mydb +``` + +### 3. Keep `.env` Out of Version Control + +The `.env` file is already in `.gitignore`. **NEVER remove it from `.gitignore`.** + +```bash +# Verify .env is ignored +git status | grep .env +# Should show nothing (file is ignored) +``` + +### 4. Use `.env.example` as Template + +The `.env.example` file is committed to Git and serves as a template. It contains: + +- ✅ Variable names and structure +- ✅ Comments explaining each variable +- ✅ Example/placeholder values (not real credentials) +- ❌ NO real passwords or API keys + +### 5. Rotate Credentials if Compromised + +If credentials are accidentally committed to Git: + +1. **Immediately change the password/API key** in database/service +2. Update `.env` files in all environments +3. Restart applications +4. Consider the old credential compromised (attackers may have Git history) + +**Git history cleanup (advanced, use with caution):** + +```bash +# Option 1: BFG Repo-Cleaner (recommended) +# https://rtyley.github.io/bfg-repo-cleaner/ + +# Option 2: git filter-branch (complex) +# Not recommended unless you know what you're doing +``` + +### 6. Use Strong Credentials + +- **Database passwords:** Minimum 16 characters, mix of letters/numbers/symbols +- **Flask SECRET_KEY:** Minimum 64 random characters +- **API Keys:** Use keys from official provider (Google Cloud Console) + +**Generate random SECRET_KEY:** + +```bash +python3 -c "import secrets; print(secrets.token_hex(32))" +``` + +### 7. Principle of Least Privilege + +- **Development:** Use separate database user with limited permissions +- **Production:** Use `nordabiz_app` user (not postgres superuser) +- **API Keys:** Enable only required APIs in Google Cloud Console + +--- + +## Verification and Testing + +### Pre-Deployment Checklist + +Before deploying any code, run these verification commands: + +```bash +# 1. Check for hardcoded passwords in Python files +grep -r "NordaBiz2025Secure" --include="*.py" . +# Expected: No results (or only in docs/) + +# 2. Check for hardcoded PGPASSWORD in shell scripts +grep -r "PGPASSWORD=" --include="*.sh" . +# Expected: No results (or only validation checks) + +# 3. Check for hardcoded DATABASE_URL with passwords +grep -r "postgresql://.*:.*@" --include="*.py" . | grep -v "CHANGE_ME" | grep -v ".example" +# Expected: No results (or only safe placeholders) + +# 4. Verify .env is in .gitignore +git check-ignore .env +# Expected: .env (file is ignored) + +# 5. Check what would be committed +git status +# Expected: .env should NOT appear in list +``` + +### Testing Environment Variable Loading + +**Test Python scripts:** + +```bash +# Unset DATABASE_URL to test fallback +unset DATABASE_URL +python3 -c "from database import get_database_url; print(get_database_url())" +# Expected: postgresql://user:CHANGE_ME@127.0.0.1:5432/nordabiz (safe fallback) + +# Set DATABASE_URL to test loading +export DATABASE_URL='postgresql://test:test@localhost:5432/testdb' +python3 -c "from database import get_database_url; print(get_database_url())" +# Expected: postgresql://test:test@localhost:5432/testdb (from environment) +``` + +**Test shell scripts:** + +```bash +# Unset PGPASSWORD to test validation +unset PGPASSWORD +./view_maturity_results.sh +# Expected: ERROR message about PGPASSWORD not set + +# Set PGPASSWORD to test execution +export PGPASSWORD='test_password' +./view_maturity_results.sh +# Expected: Script runs (may fail if credentials wrong, but validation passed) +``` + +### Continuous Monitoring + +Add these checks to your deployment pipeline: + +```bash +# In CI/CD or pre-commit hook +if grep -r "NordaBiz2025Secure" --include="*.py" --include="*.sh" .; then + echo "ERROR: Found hardcoded credentials in code" + exit 1 +fi +``` + +--- + +## Troubleshooting + +### Issue: "PGPASSWORD not set" error in shell script + +**Symptom:** +``` +ERROR: PGPASSWORD environment variable is not set +``` + +**Solution:** +```bash +# Set PGPASSWORD before running script +export PGPASSWORD='your_database_password' +./your_script.sh +``` + +### Issue: "Password authentication failed" in Python script + +**Symptom:** +``` +sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) FATAL: password authentication failed +``` + +**Diagnosis:** +```bash +# Check if DATABASE_URL is set +echo $DATABASE_URL + +# Check if DATABASE_URL in .env is correct +cat .env | grep DATABASE_URL +``` + +**Solution:** +1. Verify password is correct in `.env` +2. Ensure `.env` file exists and is readable +3. Check database user exists: `psql -U postgres -c "\du"` + +### Issue: Script uses 'CHANGE_ME' placeholder password + +**Symptom:** +``` +sqlalchemy.exc.OperationalError: FATAL: password authentication failed for user "nordabiz_app" +``` + +**Cause:** DATABASE_URL environment variable is not set, script is using safe fallback. + +**Solution:** +```bash +# Check if .env file exists +ls -la .env + +# Check if DATABASE_URL is in .env +grep DATABASE_URL .env + +# Ensure you're loading .env (Flask should do this automatically) +# For standalone scripts, may need to load manually: +python3 -c "from dotenv import load_dotenv; load_dotenv(); import os; print(os.getenv('DATABASE_URL'))" +``` + +### Issue: ".env file not found" on production + +**Symptom:** +Application can't find credentials, using fallback values. + +**Diagnosis:** +```bash +# Check if .env exists in application directory +ls -la /var/www/nordabiznes/.env + +# Check file ownership and permissions +ls -la /var/www/nordabiznes/.env +# Expected: -rw------- 1 www-data www-data +``` + +**Solution:** +```bash +# Create .env from template +cd /var/www/nordabiznes +sudo -u www-data cp .env.example .env +sudo -u www-data nano .env # Edit with production credentials + +# Set correct permissions +sudo chown www-data:www-data .env +sudo chmod 600 .env + +# Restart application +sudo systemctl restart nordabiznes +``` + +--- + +## Incident Response + +### If Credentials Are Committed to Git + +**CRITICAL: Follow these steps immediately** + +1. **Change the compromised credential** + ```bash + # For database password + psql -U postgres -d nordabiz -c "ALTER USER nordabiz_app WITH PASSWORD 'new_secure_password';" + + # For API keys + # Disable old key in Google Cloud Console and create new one + ``` + +2. **Update `.env` files in all environments** + ```bash + # Development + nano .env # Update DATABASE_URL with new password + + # Production + ssh maciejpi@10.22.68.249 + cd /var/www/nordabiznes + sudo -u www-data nano .env # Update DATABASE_URL with new password + sudo systemctl restart nordabiznes + ``` + +3. **Remove credential from Git history (optional, advanced)** + ```bash + # WARNING: This rewrites Git history and requires force push + # Coordinate with team before doing this + + # Using BFG Repo-Cleaner (recommended) + java -jar bfg.jar --replace-text passwords.txt nordabiz.git + git reflog expire --expire=now --all + git gc --prune=now --aggressive + git push --force + ``` + +4. **Document the incident** + - Create incident report in `docs/INCIDENT_REPORT_YYYYMMDD.md` + - Document what was exposed, when, and remediation steps + - Review with team to prevent future incidents + +5. **Review access logs** + ```bash + # Check PostgreSQL logs for unauthorized access + sudo tail -100 /var/log/postgresql/postgresql-14-main.log + + # Check Flask application logs + sudo journalctl -u nordabiznes -n 100 + ``` + +### Emergency Contacts + +| Role | Contact | Responsibility | +|------|---------|----------------| +| System Administrator | maciejpi@inpi.local | Infrastructure, database | +| Lead Developer | [Your contact] | Application code | +| Security Officer | [Contact if applicable] | Security incidents | + +--- + +## Additional Resources + +### Official Documentation + +- PostgreSQL Authentication: https://www.postgresql.org/docs/current/auth-methods.html +- PostgreSQL .pgpass file: https://www.postgresql.org/docs/current/libpq-pgpass.html +- Flask Configuration: https://flask.palletsprojects.com/en/2.3.x/config/ +- python-dotenv: https://github.com/theskumar/python-dotenv + +### Security Standards + +- CWE-798: Use of Hard-coded Credentials: https://cwe.mitre.org/data/definitions/798.html +- OWASP Top 10: https://owasp.org/www-project-top-ten/ +- OWASP Secrets Management Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html + +### Internal Documentation + +- Project README: `README.md` +- Developer Guide: `CLAUDE.md` +- Environment Configuration: `.env.example` +- Architecture Documentation: `docs/architecture/` + +--- + +**Document Version:** 1.0 +**Last Reviewed:** 2026-01-10 +**Next Review:** 2026-04-10 (quarterly)