CWE-526: Information Exposure Through Environment Variables
Overview
Information exposure through environment variables occurs when applications unintentionally leak sensitive configuration data, credentials, API keys, or system information stored in environment variables. This typically happens through error pages displaying environment dumps, debugging endpoints exposing process state, server-side template injection rendering environment context, or misconfigured logging that captures full environment. Environment variables are a common location for secrets in containerized and cloud environments, making their exposure particularly critical.
OWASP Classification
A02:2025 - Security Misconfiguration
Risk
High: Exposed environment variables often contain database passwords, API keys, encryption keys, OAuth client secrets, AWS credentials, and other sensitive configuration. Attackers can use this information for privilege escalation, lateral movement, data exfiltration, and complete system compromise.
Remediation Steps
Core principle: Never expose environment variables to untrusted users; use secure secret management systems and filter environment data from all user-facing outputs including errors, logs, and debug endpoints.
Remove Debug Endpoints
# VULNERABLE - exposes environment
@app.route('/debug/env')
def debug_env():
import os
return jsonify(dict(os.environ)) # DANGEROUS!
# SECURE - remove entirely or protect with authentication
# DELETE THIS ENDPOINT in production
Filter Error Messages
# VULNERABLE - error page shows environment
app.config['DEBUG'] = True # Shows stack trace + environment!
# SECURE - generic errors, no environment exposure
app.config['DEBUG'] = False
@app.errorhandler(Exception)
def handle_error(e):
# Log full error server-side (for debugging)
logger.error(f"Error: {e}", exc_info=True)
# Return generic error to user
return {'error': 'Internal server error'}, 500
Use Secret Management Systems
Don't store secrets in environment variables in production:
AWS Secrets Manager:
import boto3
def get_secret(secret_name):
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId=secret_name)
return response['SecretString']
db_password = get_secret('prod/db/password')
HashiCorp Vault:
import hvac
client = hvac.Client(url='https://vault.example.com')
secret = client.secrets.kv.v2.read_secret_version(path='prod/database')
db_password = secret['data']['data']['password']
Sanitize Logs
import logging
import re
class SecretFilter(logging.Filter):
def filter(self, record):
# Redact common secret patterns
message = record.getMessage()
message = re.sub(r'(password|api[_-]?key|token)=[^\s&]+', r'\1=***', message, flags=re.IGNORECASE)
record.msg = message
record.args = ()
return True
logger = logging.getLogger()
logger.addFilter(SecretFilter())
Prevent Server-Side Template Injection
# VULNERABLE - template injection exposes environment
from jinja2 import Template
template = Template(user_input) # Attacker: {{config.items()}}
output = template.render()
# SECURE - use autoescape, don't render user input as templates
from flask import render_template_string
output = render_template_string('<div>{{ user_data }}</div>', user_data=user_input)
Container Security
For Docker/Kubernetes:
- Use secrets management (Kubernetes Secrets, Docker secrets)
- Don't pass secrets via environment variables (use mounted secret files)
- Scan container images for exposed credentials
- Use minimal base images
# Kubernetes - use secrets instead of env vars
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secrets
secret:
secretName: app-secrets
Dynamic Scan Guidance
For guidance on remediating this CWE when detected by dynamic (DAST) scanners:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code