Created comprehensive docs/SECURITY.md with: - Database credentials management guide (CWE-798 security) - Complete environment variables reference - Development and production setup instructions - Shell script configuration (.pgpass, PGPASSWORD) - Security best practices (never hardcode credentials) - Verification and testing procedures - Troubleshooting guide for common issues - Incident response procedures for compromised credentials - Links to official documentation and security standards This completes Phase 4 (Documentation) of the credential security cleanup task.
17 KiB
Security Guide - Norda Biznes Hub
Last Updated: 2026-01-10 Status: Active Severity: CRITICAL - Follow all guidelines
Table of Contents
- Overview
- Database Credentials Management
- Environment Variables Reference
- Development Environment Setup
- Production Environment Setup
- Shell Script Configuration
- Security Best Practices
- Verification and Testing
- Troubleshooting
- 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
- Never commit credentials to Git - Use
.envfiles (already in.gitignore) - Use environment variables - All scripts read from
DATABASE_URLorPGPASSWORD - Safe fallback values - Default values use
CHANGE_MEplaceholder (will fail fast) - Fail fast - Scripts exit with clear error if credentials missing
- 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
Solution 3: Use .pgpass File (Recommended for Production)
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:
- Immediately change the password/API key in database/service
- Update
.envfiles in all environments - Restart applications
- 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_appuser (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:
- Verify password is correct in
.env - Ensure
.envfile exists and is readable - 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
-
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 -
Update
.envfiles 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 -
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 -
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
- Create incident report in
-
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
- 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)