Cybersecurity

Protecting API Keys and Secrets in AI Coding Workflows

Python LLMs MCP AI Agents Cybersecurity CI/CD Git GitHub AWS Cloud Databases SQL Rust Credentials Passwords Hashing HTTPS
1,807 words Includes Code

Protecting API Keys and Secrets in AI Coding Workflows

🔑 Key Takeaway

Never commit secrets to source control. API keys, database credentials, and tokens should be stored securely using environment variables, secret managers, or scoped credentials. Git history is permanent — even deleted files can be recovered.

⚠️ All Examples Use Synthetic Data

This article uses fake API keys and credentials for educational purposes. Never use real secrets in code examples or tutorials.

Protecting API Keys and Secrets in AI Coding Workflows

Why Secrets Security Matters

AI coding workflows often require access to external services:

  • LLM APIs — OpenAI, Anthropic, local models
  • Cloud Services — AWS, Azure, GCP credentials
  • Databases — Connection strings with passwords
  • Version Control — GitHub/GitLab tokens
  • Deployment — SSH keys, deployment tokens

❌ What Happens When Secrets Leak

  • Unauthorized access — Attackers use your credentials
  • Data breaches — Sensitive data exposed
  • Financial loss — Billable API usage by attackers
  • Reputation damage — Loss of trust
  • Compliance violations — Regulatory penalties

The Danger of .env Files

.env files are a common way to store secrets locally, but they come with risks:

❌ Common .env Mistakes

# ❌ NEVER commit .env to Git
# Even if you delete it later, it's in history

# .env (should be in .gitignore)
OPENAI_API_KEY=sk-real-api-key-here
DATABASE_URL=postgresql://user:password@host/db
AWS_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx

The fix: Always add .env to .gitignore

# .gitignore — ALWAYS include this
.env
.env.local
.env.*.local
*.env
.env.production

# Also ignore other potential secret files
*.pem
*.key
*.p12
credentials.json
service-account*.json

Environment Variables: The First Line of Defense

Environment variables keep secrets out of your codebase:

✓ Setting Environment Variables

# Method 1: Set in terminal (temporary)
$ export OPENAI_API_KEY="sk-fake-key-for-demo"

# Method 2: Use .env file (never commit)
$ source .env

# Method 3: Set per-command
$ OPENAI_API_KEY="sk-fake" python script.py

# Method 4: Use direnv (auto-load per directory)
$ direnv allow

✓ Reading Environment Variables in Python

import os

# Read API key from environment
api_key = os.environ.get("OPENAI_API_KEY")

if not api_key:
    raise ValueError(
        "OPENAI_API_KEY environment variable not set. "
        "Please set it before running this script."
    )

# Use the key
client = OpenAI(api_key=api_key)

# ❌ NEVER do this:
# client = OpenAI(api_key="sk-real-key-here")

Secret Managers: Enterprise-Grade Protection

For production systems and team environments, use a dedicated secret manager:

Tool Type Best For Cost
HashiCorp Vault Self-hosted/Cloud Enterprise, complex policies Free OSS / Paid cloud
AWS Secrets Manager Cloud AWS workloads Pay per secret
Azure Key Vault Cloud Azure workloads Pay per operation
GCP Secret Manager Cloud GCP workloads Pay per operation
1Password CLI Commercial Individual/small teams Subscription
pass Self-hosted Unix users, simple Free

✓ Example: Using a Secret Manager

# Example: AWS Secrets Manager (Python)
import boto3
import json

def get_secret(secret_name: str) -> dict:
    """Retrieve secret from AWS Secrets Manager."""
    client = boto3.client('secretsmanager')
    
    response = client.get_secret_value(SecretId=secret_name)
    return json.loads(response['SecretString'])

# Usage
db_credentials = get_secret("prod/database/credentials")
connection = connect(
    host=db_credentials["host"],
    user=db_credentials["username"],
    password=db_credentials["password"]
)

Scoped Credentials: Minimum Required Access

Give credentials only the permissions they need:

✓ Scoped API Keys

# ❌ BAD: Admin key with full access
OPENAI_API_KEY=sk-admin-key-with-unlimited-access

# ✓ BETTER: Scoped key with limits
OPENAI_API_KEY=sk-scoped-key-with-usage-limits
# Configured with:
# - Rate limits
# - Spending caps
# - Model restrictions
# - IP allowlisting

# ✓ BEST: Service-specific key
OPENAI_API_KEY=sk-development-only-key
# Only works in development environment
Scope Type Example Protection
Environment Dev vs Production keys Separate credentials per environment
Permission Read-only vs Read-write Limit what the key can do
Time Short-lived tokens Auto-expire after use
IP IP allowlisting Only work from approved IPs

Short-Lived Credentials

Use tokens that expire automatically to limit exposure:

✓ Short-Lived Token Pattern

# Example: Short-lived credentials for CI/CD
# GitHub Actions with OIDC (no long-lived tokens)

# .github/workflows/deploy.yml
jobs:
  deploy:
    permissions:
      id-token: write  # OIDC token
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/deploy
          aws-region: us-east-1
      # Credentials auto-expire after job

💡 Why Short-Lived?

If a short-lived token leaks, it expires automatically. Long-lived credentials remain valid until manually revoked — and attackers may use them for months before detection.

Secret Scanning: Catch Leaks Before They Happen

Automated tools can detect secrets before they're committed:

Tool Type Best For
GitHub Secret Scanning Cloud GitHub repositories
TruffleHog CLI/CI Git history scanning
GitLeaks CLI/CI Pre-commit hooks
detect-secrets CLI Python projects
tfsec CLI Terraform secrets

✓ Setting Up Pre-Commit Secret Scanning

# Install gitleaks
$ brew install gitleaks  # macOS
$ pip install gitleaks  # or use binary

# Set up pre-commit hook
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

# Scan entire repository
$ gitleaks detect --source . --verbose

# Scan git history
$ gitleaks git --log-opts="--all" --verbose

Safe Logging Practices

Never log secrets, even in debug output:

# ❌ BAD: Logging secrets
logger.info(f"API Key: {api_key}")
print(f"Connecting with: {db_url}")
logger.debug(f"Token: {auth_token}")

# ✓ BETTER: Log without secrets
logger.info("API connection established")
print("Connecting to database...")
logger.debug(f"Token length: {len(auth_token)}")

# ✓ BEST: Use structured logging with redaction
import logging

class SecretFilter(logging.Filter):
    def filter(self, record):
        # Redact common secret patterns
        if hasattr(record, 'msg'):
            record.msg = re.sub(
                r'(sk-[a-zA-Z0-9]{20})[a-zA-Z0-9]*',
                r'\1[REDACTED]',
                record.msg
            )
        return True

Complete Secret Security Checklist

✅ 15-Point Secret Security Checklist

Never hardcode secrets in source code
Use environment variables or secret managers
Add .env to .gitignore
Prevent accidental commits
Use secret managers for production
HashiCorp Vault, AWS Secrets Manager, etc.
Scope credentials to minimum required access
Read-only, specific resources, time-limited
Use short-lived tokens where possible
Auto-expiring credentials reduce blast radius
Enable secret scanning
GitHub, TruffleHog, GitLeaks
Set up pre-commit hooks
Block commits containing secrets
Rotate credentials regularly
Don't use the same key forever
Never log secrets
Redact sensitive data in logs
Use separate credentials per environment
Dev, staging, production should be different
Monitor API usage
Detect unexpected key usage
Have a revocation plan
Know how to quickly disable compromised keys
Document secrets inventory
Know what keys exist and where they're used
Train team members
Everyone should know secret security basics
Audit access regularly
Review who has access to what

Related BestWordz Resources

Conclusion

Protecting API keys and secrets is a fundamental security practice for AI coding workflows.

Key principles:

  • Never commit secrets to source control
  • Use environment variables or secret managers
  • Scope credentials to minimum required access
  • Use short-lived tokens where possible
  • Enable secret scanning in your workflow
  • Monitor and rotate credentials regularly

Git history is permanent. Once a secret is committed, it can be recovered even after deletion. Prevention is always better than remediation.

💬 Discuss on BestWordz Community

Join the conversation about Python, LLMs, MCP on the BestWordz Community forum.

Visit Forum →