Skip to content

SECURITY: API Key Exposure via Subprocess Environment Variables #55

@jeremyeder

Description

@jeremyeder

Vulnerability Summary

Severity: MEDIUM (CVSS 5.3)
CWE: CWE-200 (Information Exposure), CWE-526 (Exposure of Sensitive Information Through Environmental Variables)
Location: src/agentready/learners/llm_enricher.py
Impact: API keys leaked through process listings and error messages

Description

The Anthropic API client may expose API keys through environment variables that are visible to:

  • Process monitoring tools (ps aux, top)
  • Error messages and stack traces
  • Log files
  • Child processes

Vulnerability Analysis

# llm_enricher.py - API client initialization
from anthropic import Anthropic

# If ANTHROPIC_API_KEY is set in environment, it's visible to all processes
client = Anthropic()  # Reads from os.environ['ANTHROPIC_API_KEY']

Information Leakage Vectors

  1. Process listings: Environment variables visible in /proc/PID/environ
  2. Error messages: API errors may include partial keys
  3. Log files: Logger may capture API responses with keys
  4. Child processes: Subprocess inherits environment

Example Attack

# Attacker on same system
ps aux | grep agentready
cat /proc/$(pgrep -f agentready)/environ | tr '\0' '\n' | grep ANTHROPIC

Security Impact

  • API key theft: Attackers gain access to Claude API
  • Cost fraud: Unauthorized API usage charged to victim
  • Data exfiltration: Use API to process sensitive data
  • Quota exhaustion: Denial of service via rate limit depletion

Remediation

Immediate Fix (P1)

  1. Clear API key from environment after reading:
# SECURITY: API Key Handling - Clear from environment after use
# Why: Environment variables are visible in process listings
# Prevents: Information Exposure (CWE-200)
# Alternative considered: Keyring storage rejected due to complexity

import os
from anthropic import Anthropic

class LLMEnricher:
    def __init__(self, client: Anthropic | None = None, ...):
        if client is None:
            # Read and immediately clear from environment
            api_key = os.environ.get('ANTHROPIC_API_KEY')
            if api_key:
                # Clear from environment to prevent leakage
                del os.environ['ANTHROPIC_API_KEY']
                client = Anthropic(api_key=api_key)
                # Don't store raw key, only client
            else:
                raise ValueError("ANTHROPIC_API_KEY environment variable not set")
        
        self.client = client
  1. Sanitize error messages:
try:
    response = self.client.messages.create(...)
except APIError as e:
    # SECURITY: Strip potential API key from error message
    error_msg = str(e)
    if 'sk-ant-' in error_msg:
        error_msg = re.sub(r'sk-ant-[a-zA-Z0-9-]+', 'sk-ant-***REDACTED***', error_msg)
    logger.error(f"API error enriching {skill.skill_id}: {error_msg}")
    return skill
  1. Secure logging configuration:
# SECURITY: Configure logging to exclude sensitive data
import logging

class SensitiveDataFilter(logging.Filter):
    """Filter to redact API keys and secrets from logs."""
    
    PATTERNS = [
        (re.compile(r'sk-ant-[a-zA-Z0-9-]+'), 'sk-ant-***'),
        (re.compile(r'ANTHROPIC_API_KEY[=:]\\s*\\S+'), 'ANTHROPIC_API_KEY=***'),
    ]
    
    def filter(self, record):
        record.msg = self._redact(str(record.msg))
        if record.args:
            record.args = tuple(self._redact(str(arg)) for arg in record.args)
        return True
    
    def _redact(self, text: str) -> str:
        for pattern, replacement in self.PATTERNS:
            text = pattern.sub(replacement, text)
        return text

# Apply filter to all loggers
for handler in logging.root.handlers:
    handler.addFilter(SensitiveDataFilter())

Additional Protections

  1. Use keyring for secure storage:

    import keyring
    
    # Store API key securely
    keyring.set_password("agentready", "anthropic_api_key", api_key)
    
    # Retrieve when needed
    api_key = keyring.get_password("agentready", "anthropic_api_key")
  2. Document secure usage:

    # Security Best Practices
    
    **API Key Management**:
    - Never commit API keys to git
    - Use `.env` files (excluded from git)
    - Clear `ANTHROPIC_API_KEY` after use
    - Rotate keys regularly
    - Use separate keys for development/production
  3. Add security checks:

    # Warn if API key is set globally
    if 'ANTHROPIC_API_KEY' in os.environ:
        warnings.warn(
            "ANTHROPIC_API_KEY is set in environment. "
            "Consider using .env file instead for security.",
            SecurityWarning
        )

References

Related Issues

  • Subprocess calls may leak environment to child processes
  • Error messages may contain sensitive repository paths

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions