# Security Guide - Norda Biznes Partner **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 Partner 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@57.128.200.27 ``` **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 — OVH VPS 57.128.200.27) 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 root (OVH VPS — .env is root-owned) sudo chown root:root /var/www/nordabiznes/.env sudo chmod 600 /var/www/nordabiznes/.env # Verify permissions ls -la /var/www/nordabiznes/.env # Expected: -rw------- 1 root root ``` ### 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 57.128.200.27: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 (OVH VPS) ssh maciejpi@57.128.200.27 cd /var/www/nordabiznes sudo nano .env # Update DATABASE_URL with new password (.env is root-owned) 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)