Secrets & Credential Management

Secure your API keys, passwords, and sensitive configuration

Security keys and credentials protection
By FixTheVuln Team Peer-reviewed security content Sources: CISA, NVD, OWASP

Key Takeaways

  • Never hardcode secrets in source code — use environment variables or secrets managers
  • Use a dedicated secrets manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault)
  • Rotate secrets regularly and automatically where possible
  • Add .env files to .gitignore to prevent accidental commits
  • Use git-secrets or truffleHog to scan for accidentally committed credentials
Why Secrets Management Matters: Exposed credentials are the #1 cause of data breaches. API keys, database passwords, and encryption keys accidentally committed to Git or hardcoded in source code lead to devastating compromises.

What NOT to Do

1. Never Hardcode Secrets

NEVER do this:
# BAD - Hardcoded credentials in source code database_password = "SuperSecret123!" api_key = "sk-1234567890abcdef" # BAD - Credentials in config files committed to Git # config.py DB_HOST = "localhost" DB_USER = "admin" DB_PASS = "password123" # NEVER! # BAD - Connection strings with embedded passwords connection_string = "mysql://root:password@localhost/mydb"

What Attackers Find in Public Git Repos

Secret Type Risk Level Impact
AWS Access Keys Critical Full cloud account takeover, crypto mining
Database Passwords Critical Complete data breach
API Keys (Stripe, Twilio) High Financial fraud, service abuse
JWT Secrets Critical Token forgery, authentication bypass
SSH Private Keys Critical Server access, lateral movement

Environment Variables

2. Use Environment Variables

Setting Environment Variables

# Linux/macOS - Temporary (current session) export DATABASE_PASSWORD="SecurePassword123!" export API_KEY="sk-1234567890abcdef" # Linux/macOS - Permanent (add to ~/.bashrc or ~/.zshrc) echo 'export DATABASE_PASSWORD="SecurePassword123!"' >> ~/.bashrc source ~/.bashrc # Windows Command Prompt set DATABASE_PASSWORD=SecurePassword123! # Windows PowerShell $env:DATABASE_PASSWORD = "SecurePassword123!" # Windows - Permanent (System Properties > Environment Variables) setx DATABASE_PASSWORD "SecurePassword123!"

Reading Environment Variables in Code

# Python import os db_password = os.environ.get('DATABASE_PASSWORD') api_key = os.getenv('API_KEY', 'default_value') # Node.js const dbPassword = process.env.DATABASE_PASSWORD; const apiKey = process.env.API_KEY || 'default_value'; # PHP $db_password = getenv('DATABASE_PASSWORD'); $api_key = $_ENV['API_KEY'] ?? 'default_value'; # Java String dbPassword = System.getenv("DATABASE_PASSWORD"); # C# / .NET string dbPassword = Environment.GetEnvironmentVariable("DATABASE_PASSWORD"); # Go dbPassword := os.Getenv("DATABASE_PASSWORD") # Ruby db_password = ENV['DATABASE_PASSWORD']

.env Files

3. Using .env Files Securely

Create .env File

# .env file (in project root) DATABASE_HOST=localhost DATABASE_PORT=5432 DATABASE_NAME=myapp DATABASE_USER=app_user DATABASE_PASSWORD=SecurePassword123! API_KEY=sk-1234567890abcdef JWT_SECRET=super-secret-jwt-key-change-this # AWS Credentials AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY # Third-party APIs STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxx SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxx
CRITICAL: Add .env to .gitignore!

.gitignore Configuration

# .gitignore - MUST include these .env .env.local .env.*.local .env.production .env.development # Also ignore *.pem *.key *.p12 config/secrets.yml config/credentials.yml.enc # IDE files that might contain secrets .idea/ .vscode/settings.json

Loading .env Files

# Python - using python-dotenv pip install python-dotenv from dotenv import load_dotenv import os load_dotenv() # Loads .env file db_password = os.getenv('DATABASE_PASSWORD') # Node.js - using dotenv npm install dotenv require('dotenv').config(); const dbPassword = process.env.DATABASE_PASSWORD; # PHP - using vlucas/phpdotenv composer require vlucas/phpdotenv $dotenv = Dotenv\Dotenv::createImmutable(__DIR__); $dotenv->load(); $dbPassword = $_ENV['DATABASE_PASSWORD']; # Ruby - using dotenv gem install dotenv require 'dotenv/load' db_password = ENV['DATABASE_PASSWORD']

Create .env.example Template

# .env.example (commit this to Git as a template) DATABASE_HOST=localhost DATABASE_PORT=5432 DATABASE_NAME=myapp DATABASE_USER= DATABASE_PASSWORD= API_KEY= JWT_SECRET= # Instructions for developers: # 1. Copy this file to .env # 2. Fill in the actual values # 3. NEVER commit .env to Git

Secret Scanning

4. Scan for Leaked Secrets

git-secrets (AWS)

# Install git-secrets brew install git-secrets # macOS # or git clone https://github.com/awslabs/git-secrets.git cd git-secrets && make install # Set up in repository cd your-repo git secrets --install git secrets --register-aws # Scan existing commits git secrets --scan-history # Add to pre-commit hook (automatic scanning) git secrets --install

TruffleHog

# Install TruffleHog pip install trufflehog # Scan Git repository trufflehog git https://github.com/user/repo.git # Scan local directory trufflehog filesystem /path/to/project # Scan with JSON output trufflehog git https://github.com/user/repo.git --json

Gitleaks

# Install Gitleaks brew install gitleaks # macOS # or download from GitHub releases # Scan repository gitleaks detect --source /path/to/repo # Scan and generate report gitleaks detect --source /path/to/repo --report-path report.json # Use in CI/CD gitleaks detect --source . --verbose

GitHub Secret Scanning

GitHub automatically scans public repos for known secret patterns and alerts you. Enable in: Repository Settings → Security & Analysis → Secret Scanning

Secrets Managers

5. Use a Secrets Manager (Production)

HashiCorp Vault

# Install Vault brew install vault # macOS # Start dev server (testing only) vault server -dev # Store a secret vault kv put secret/myapp/database password="SecurePass123!" # Retrieve a secret vault kv get secret/myapp/database # Use in application (Python) import hvac client = hvac.Client(url='https://vault.example.com:8200') client.token = 'your-vault-token' secret = client.secrets.kv.read_secret_version(path='myapp/database') db_password = secret['data']['data']['password']

AWS Secrets Manager

# Create secret via AWS CLI aws secretsmanager create-secret \ --name myapp/database \ --secret-string '{"username":"admin","password":"SecurePass123!"}' # Retrieve secret via CLI aws secretsmanager get-secret-value --secret-id myapp/database # Python (boto3) import boto3 import json client = boto3.client('secretsmanager', region_name='us-east-1') response = client.get_secret_value(SecretId='myapp/database') secret = json.loads(response['SecretString']) db_password = secret['password'] # Node.js const AWS = require('aws-sdk'); const client = new AWS.SecretsManager({ region: 'us-east-1' }); const data = await client.getSecretValue({ SecretId: 'myapp/database' }).promise(); const secret = JSON.parse(data.SecretString); const dbPassword = secret.password;

Azure Key Vault

# Create Key Vault az keyvault create --name myapp-vault --resource-group mygroup # Store secret az keyvault secret set --vault-name myapp-vault --name db-password --value "SecurePass123!" # Retrieve secret az keyvault secret show --vault-name myapp-vault --name db-password # Python from azure.identity import DefaultAzureCredential from azure.keyvault.secrets import SecretClient credential = DefaultAzureCredential() client = SecretClient(vault_url="https://myapp-vault.vault.azure.net/", credential=credential) secret = client.get_secret("db-password") db_password = secret.value

Google Cloud Secret Manager

# Create secret echo -n "SecurePass123!" | gcloud secrets create db-password --data-file=- # Access secret gcloud secrets versions access latest --secret=db-password # Python from google.cloud import secretmanager client = secretmanager.SecretManagerServiceClient() name = "projects/my-project/secrets/db-password/versions/latest" response = client.access_secret_version(request={"name": name}) db_password = response.payload.data.decode("UTF-8")

Docker Secrets

6. Secrets in Containers

Never put secrets in Dockerfiles or docker-compose.yml!

Docker Secrets (Swarm)

# Create secret echo "SecurePass123!" | docker secret create db_password - # Use in docker-compose.yml version: '3.8' services: app: image: myapp secrets: - db_password environment: - DB_PASSWORD_FILE=/run/secrets/db_password secrets: db_password: external: true

Kubernetes Secrets

# Create secret kubectl create secret generic db-credentials \ --from-literal=username=admin \ --from-literal=password=SecurePass123! # Or from file kubectl create secret generic db-credentials --from-env-file=.env # Use in Pod apiVersion: v1 kind: Pod metadata: name: myapp spec: containers: - name: app image: myapp envFrom: - secretRef: name: db-credentials # Or individual keys env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-credentials key: password

What To Do If Secrets Are Exposed

7. Emergency Response

# 1. IMMEDIATELY rotate the exposed credential # - Generate new API key/password # - Update in secrets manager # - Deploy to production # 2. Revoke the old credential # - Disable/delete old API keys # - Change passwords # - Rotate JWT secrets # 3. Remove from Git history (if committed) # Using BFG Repo-Cleaner (faster than git filter-branch) brew install bfg # Remove file containing secrets bfg --delete-files .env # Remove specific text bfg --replace-text passwords.txt # Clean up git reflog expire --expire=now --all git gc --prune=now --aggressive # Force push (coordinate with team!) git push --force # 4. Audit for unauthorized access # - Check logs for API usage # - Review database access logs # - Check cloud provider activity logs # 5. Scan entire codebase gitleaks detect --source . --verbose trufflehog filesystem .

Security Checklist

☐ No hardcoded secrets in source code
☐ .env files added to .gitignore
☐ .env.example template provided for developers
☐ Environment variables used for configuration
☐ Secret scanning enabled in CI/CD pipeline
☐ Pre-commit hooks prevent secret commits
☐ Production uses secrets manager (Vault, AWS, etc.)
☐ Secrets rotated regularly
☐ Least privilege access to secrets
☐ Audit logging enabled for secret access
☐ Emergency rotation procedure documented

Remember: If a secret is ever exposed (even briefly), treat it as compromised and rotate immediately. Attackers have automated tools scanning GitHub for secrets 24/7.

Related Resources

Encryption Cheatsheet API Security Container Security

Test Your Knowledge

Security+ Practice Quiz HashiCorp Vault Practice Quiz
← Back to Tools

FixTheVuln Store

Get the HashiCorp Vault Study Planner

Fillable PDF study planners with domain trackers, weekly schedules, and progress tracking. Available in Standard, ADHD-Friendly, Dark Mode, and 4-Format Bundle.

HashiCorp Vault Planner

60+ certifications available — from $5.99

CyberFolio

Choosing your next cert? Track them all in one place.

Build a shareable cybersecurity portfolio that highlights your certifications, projects, and skills — free.

Build Your Portfolio →