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

91 lines
2.4 KiB
Markdown

# Credential Handling
## Rule
Never hardcode secrets. Load from environment or secret manager at runtime.
**Source:** [CWE-798: Use of Hard-coded Credentials](https://cwe.mitre.org/data/definitions/798.html)
## Correct Pattern
```python
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
```python
# 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:
```python
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