Files
security-patterns/credential-handling.md
Rodin 647928a0a1 Initial commit: 9 security patterns for code review
Fundamentals: secure-defaults, input-validation, credential-handling, audit-logging
Identity: authentication, authorization
Attack Prevention: injection-prevention, dos-prevention, prompt-injection
2026-05-10 22:45:03 -07:00

2.4 KiB

Credential Handling

Rule

Never hardcode secrets. Load from environment or secret manager at runtime.

Source: CWE-798: Use of Hard-coded Credentials

Correct Pattern

import os
from functools import lru_cache

@lru_cache(maxsize=1)
def get_api_key() -> str:
    """Load API key from environment. Fail fast if missing."""
    key = os.environ.get("API_KEY")
    if not key:
        raise RuntimeError("API_KEY environment variable not set")
    return key

# For cloud environments, use secret manager
def get_secret(name: str) -> str:
    """Load secret from cloud secret manager."""
    from google.cloud import secretmanager
    client = secretmanager.SecretManagerServiceClient()
    response = client.access_secret_version(name=name)
    return response.payload.data.decode("UTF-8")

Incorrect Pattern

# Wrong: hardcoded secret
API_KEY = "sk-1234567890abcdef"

# Wrong: secret in config file checked into git
config = {"api_key": "sk-1234567890abcdef"}

# Wrong: secret in default argument
def call_api(key="sk-1234567890abcdef"):
    ...

# Wrong: secret in error message
def validate_key(key):
    if key != expected_key:
        raise ValueError(f"Invalid key: {key}")  # Leaks the key!

# Wrong: secret in log
logging.info(f"Using API key: {api_key}")

Secret Detection

Block these patterns in CI:

import re

SECRET_PATTERNS = [
    r'(?i)(api[_-]?key|apikey)\s*[=:]\s*["\'][^"\']+["\']',
    r'(?i)(secret|password|passwd|pwd)\s*[=:]\s*["\'][^"\']+["\']',
    r'(?i)bearer\s+[a-zA-Z0-9_-]+',
    r'sk-[a-zA-Z0-9]{32,}',  # OpenAI-style keys
    r'ghp_[a-zA-Z0-9]{36}',  # GitHub PAT
]

def scan_for_secrets(content: str) -> list[str]:
    findings = []
    for pattern in SECRET_PATTERNS:
        if re.search(pattern, content):
            findings.append(f"Potential secret: {pattern}")
    return findings

Environment Separation

Environment Source Notes
Development .env file (gitignored) Never commit
CI CI secrets / vault Injected at runtime
Production Secret manager Rotated automatically

Edge Cases

  • Secrets in Docker build args leak to image history
  • Environment variables visible in /proc on Linux
  • Secrets in URLs get logged by proxies/load balancers
  • Clipboard managers may capture pasted secrets