nordabiz/docs/SECURITY.md
Maciej Pienczyn cebe52f303 refactor: Rebranding i aktualizacja modelu AI
- Zmiana nazwy: "Norda Biznes Hub" → "Norda Biznes Partner"
- Aktualizacja modelu AI: Gemini 2.0 Flash → Gemini 3 Flash
- Zachowano historyczne odniesienia w timeline i dokumentacji

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 14:08:39 +01:00

17 KiB

Security Guide - Norda Biznes Partner

Last Updated: 2026-01-10 Status: Active Severity: CRITICAL - Follow all guidelines


Table of Contents

  1. Overview
  2. Database Credentials Management
  3. Environment Variables Reference
  4. Development Environment Setup
  5. Production Environment Setup
  6. Shell Script Configuration
  7. Security Best Practices
  8. Verification and Testing
  9. Troubleshooting
  10. 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

# In project root directory
cp .env.example .env

Step 2: Configure Database Credentials

Edit .env and set your local database credentials:

# 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

# Start PostgreSQL in Docker
docker compose up -d

# Verify database is running
docker ps | grep postgres

Step 4: Verify Configuration

# 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

# Activate virtual environment
source venv/bin/activate

# Run Flask application
python3 app.py

Production Environment Setup

Step 1: SSH to Production Server

ssh maciejpi@10.22.68.249

IMPORTANT: Always SSH as maciejpi, NEVER as root!

Step 2: Configure Production .env

# 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

# 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

# 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

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)

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

# Set PGPASSWORD only for this command
PGPASSWORD='your_database_password' ./view_maturity_results.sh

The .pgpass file allows PostgreSQL tools to authenticate without environment variables.

Step 1: Create .pgpass file

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

chmod 600 ~/.pgpass

# Verify
ls -la ~/.pgpass
# Expected: -rw------- 1 your_user your_group

Step 4: Run script (no PGPASSWORD needed)

./view_maturity_results.sh
# PostgreSQL will automatically read credentials from .pgpass

Script Validation

All shell scripts now validate that PGPASSWORD is set before execution:

# 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:

# NEVER DO THIS
DATABASE_URL = 'postgresql://user:MyPassword123@localhost/db'

CORRECT - Environment variable with safe fallback:

# 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:

# NEVER DO THIS
PGPASSWORD='MyPassword123' psql -h localhost -U myuser -d mydb

CORRECT - Use environment variable:

# 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.

# 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):

# 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:

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:

# 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:

# 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:

# 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:

# 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:

# 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:

# 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:

# 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:

# 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:

# 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

    # 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

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

    # 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

    # 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

Security Standards

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)