Inclusion of Sensitive Information in Source Code

Description

Inclusion of Sensitive Information in Source Code is a vulnerability where source code contains sensitive information that should not be accessible to users. Source code on web servers, in repositories, or included in distributed applications often contains credentials, API keys, internal paths, configuration details, and other sensitive data. When this source code is accessible to users through misconfigured servers, repository exposure, client-side code inspection, or error conditions, attackers can extract this sensitive information to compromise systems or understand application internals for further attacks.

Risk

Source code exposure creates significant security risks because attackers gaining access can understand application logic and extract sensitive data. Hardcoded credentials provide direct system access. API keys enable unauthorized service usage. Internal paths reveal system architecture. Code comments may describe vulnerabilities or security workarounds. Database connection strings expose backend systems. Configuration values reveal security settings. Even metadata like version numbers and hostnames help attackers identify vulnerable components. The risk is amplified because source code changes infrequently and exposed secrets may remain valid long after discovery.

Solution

Remove all sensitive information from source code before deployment. Use environment variables or secure configuration management for credentials. Implement code review processes to detect embedded secrets. Use static analysis tools that scan for common secret patterns (API keys, passwords, tokens). Configure web servers to prevent source code download. Keep configuration files outside web-accessible directories. Remove HTML comments containing technical details. Implement secret scanning in CI/CD pipelines to prevent commits containing sensitive data. Use dedicated secrets management solutions instead of hardcoding credentials.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Application Data - Attackers can access credentials, API keys, internal paths, and other sensitive information embedded in accessible source code.

Example Code

Vulnerable Code

<?php
// Vulnerable: Database credentials in source code
// /var/www/html/includes/db_config.inc
// (may be served as plaintext if .inc extension not handled)

$db_host = 'prod-db.company.internal';
$db_user = 'app_admin';
$db_password = 'Pr0duction_P@ssw0rd_2024!';  // Exposed!
$db_name = 'customer_data';

// Vulnerable: API keys in source
$stripe_secret_key = 'sk_live_51ABC123XYZ789';  // Live key exposed!
$stripe_publishable_key = 'pk_live_51ABC123';
$aws_access_key = 'AKIAIOSFODNN7EXAMPLE';
$aws_secret_key = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY';

// Vulnerable: Internal endpoints in code
$internal_api_url = 'http://10.0.0.50:8080/internal/api';
$admin_panel_url = 'https://admin.internal.company.com';
?>
<!-- Vulnerable: Sensitive information in HTML comments -->
<!DOCTYPE html>
<html>
<head>
    <title>Company Portal</title>
    <!-- TODO: Remove before production -->
    <!-- Database: prod-db.company.com:3306/customers -->
    <!-- Admin password: AdminPass123! -->
    <!-- API endpoint: /api/v2/internal/users -->

    <!--
    FIXME: SQL injection vulnerability in getUserById()
    Temporary workaround: sanitize input manually
    Bug ticket: JIRA-1234
    -->
</head>
<body>
    <!-- Debug info - remove later -->
    <!-- Server: web-prod-03.company.internal -->
    <!-- Build: 2024.01.15-abc123 -->
    <!-- Config: /etc/app/production.conf -->
</body>
</html>
// Vulnerable: Secrets in client-side JavaScript
// /var/www/html/js/app.js (accessible to all users!)

const config = {
    // Vulnerable: API keys in client-side code
    apiKey: 'sk_live_51ABC123XYZ789',
    apiSecret: 'shpss_secret_token_12345',

    // Vulnerable: Internal URLs exposed
    apiEndpoint: 'https://api.company.com',
    adminEndpoint: 'https://admin.internal.company.com',

    // Vulnerable: OAuth credentials
    oauthClientId: 'client_id_production',
    oauthClientSecret: 'client_secret_should_be_private',

    // Vulnerable: Encryption key in code
    encryptionKey: 'AES256-key-for-production-data'
};

// Vulnerable: Debugging code left in production
function debugLogin(username) {
    console.log('Attempting login for:', username);
    console.log('Using API key:', config.apiKey);

    // Vulnerable: Backdoor for testing
    if (username === 'debug_admin') {
        return { authenticated: true, role: 'admin' };
    }
}
// Vulnerable: Java class with embedded credentials
public class VulnerableConfig {

    // Vulnerable: Hardcoded database credentials
    private static final String DB_URL = "jdbc:mysql://prod-db:3306/customers";
    private static final String DB_USER = "db_admin";
    private static final String DB_PASSWORD = "ProductionPassword123!";

    // Vulnerable: API keys as constants
    public static final String PAYMENT_API_KEY = "sk_live_payment_key";
    public static final String ANALYTICS_KEY = "UA-12345678-1";

    // Vulnerable: Internal server IPs
    private static final String[] INTERNAL_SERVERS = {
        "10.0.0.10",  // Database server
        "10.0.0.20",  // Cache server
        "10.0.0.30",  // File server
    };

    // Vulnerable: Comment with security info
    // Default admin credentials: admin/admin123
    // LDAP server: ldap.company.internal:389
    // SSH key location: /home/deploy/.ssh/id_rsa
}
# Vulnerable: Python configuration with secrets
# config.py (in repository)

# Vulnerable: Hardcoded credentials
DATABASE_URL = "postgresql://admin:[email protected]:5432/production"

# Vulnerable: API tokens
GITHUB_TOKEN = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
SLACK_WEBHOOK = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
SENDGRID_API_KEY = "SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Vulnerable: Private keys
JWT_SECRET = "super-secret-jwt-key-for-production"
ENCRYPTION_KEY = b'Sixteen byte key'

# Vulnerable: Internal URLs
INTERNAL_API = "http://internal-api.company.local:8080"
ADMIN_DASHBOARD = "https://admin.company.internal"

Fixed Code

<?php
// Fixed: Configuration loaded from environment
// /var/www/html/includes/db_config.php

// Fixed: Load from environment variables
$db_host = getenv('DB_HOST');
$db_user = getenv('DB_USER');
$db_password = getenv('DB_PASSWORD');
$db_name = getenv('DB_NAME');

// Fixed: Validate configuration is loaded
if (empty($db_host) || empty($db_user) || empty($db_password)) {
    error_log("Database configuration not properly set");
    die("Configuration error");  // Don't reveal details
}

// Fixed: API keys from secure configuration
$stripe_secret_key = getenv('STRIPE_SECRET_KEY');
$aws_access_key = getenv('AWS_ACCESS_KEY_ID');
$aws_secret_key = getenv('AWS_SECRET_ACCESS_KEY');

// Fixed: No internal URLs in code
// Use service discovery or configuration management
?>
<!-- Fixed: No sensitive information in HTML -->
<!DOCTYPE html>
<html>
<head>
    <title>Company Portal</title>
    <!-- Fixed: No technical comments in production HTML -->
    <!-- All configuration loaded server-side -->
</head>
<body>
    <!-- Fixed: Build version from config, not hardcoded -->
    <meta name="version" content="<?php echo htmlspecialchars(getenv('BUILD_VERSION') ?: 'dev'); ?>">
</body>
</html>
// Fixed: Client-side code without secrets
// /var/www/html/js/app.js

const config = {
    // Fixed: Only public configuration in client-side code
    apiEndpoint: '/api/v1',  // Relative path, no internal URLs

    // Fixed: Client ID is public, secret stays server-side
    oauthClientId: 'public_client_id',
    // No client secret in frontend!

    // Fixed: Feature flags, no sensitive data
    features: {
        darkMode: true,
        newDashboard: false
    }
};

// Fixed: No debug code in production
// Use build process to strip debug code:
// if (process.env.NODE_ENV !== 'production') { ... }

// Fixed: Authentication through secure server endpoint
async function login(username, password) {
    const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
    });
    return response.json();
}
// Fixed: Java configuration from environment/secrets manager
public class SecureConfig {

    // Fixed: Load from environment
    private static final String DB_URL;
    private static final String DB_USER;
    private static final String DB_PASSWORD;

    static {
        DB_URL = System.getenv("DATABASE_URL");
        DB_USER = System.getenv("DATABASE_USER");
        DB_PASSWORD = System.getenv("DATABASE_PASSWORD");

        // Fixed: Validate configuration
        if (DB_URL == null || DB_USER == null || DB_PASSWORD == null) {
            throw new IllegalStateException("Database configuration not set");
        }
    }

    // Fixed: Use secrets manager for sensitive data
    public static String getApiKey(String keyName) {
        return SecretsManager.getSecret(keyName);
    }

    // Fixed: No hardcoded servers
    public static String[] getInternalServers() {
        return ServiceDiscovery.getServers("internal");
    }

    // Fixed: No sensitive comments
    // All documentation in separate, non-deployed docs
}
# Fixed: Python configuration from environment
# config.py

import os
from functools import lru_cache

# Fixed: Load from environment with validation
def get_required_env(key):
    """Get required environment variable or raise error."""
    value = os.environ.get(key)
    if not value:
        raise ValueError(f"Required environment variable {key} not set")
    return value

# Fixed: Lazy loading from environment
@lru_cache()
def get_database_url():
    return get_required_env('DATABASE_URL')

@lru_cache()
def get_api_key(service):
    return get_required_env(f'{service.upper()}_API_KEY')

# Fixed: Use secrets manager for sensitive data
from secrets_manager import SecretsClient

secrets = SecretsClient()

def get_jwt_secret():
    return secrets.get('jwt_secret')

def get_encryption_key():
    return secrets.get('encryption_key')

# Fixed: No hardcoded URLs or internal paths
# Use service discovery or configuration management
# Fixed: CI/CD pipeline with secret scanning
name: Build
on: [push, pull_request]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      # Fixed: Scan for secrets in code
      - name: Scan for secrets
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./
          base: main

      # Fixed: Check for hardcoded credentials patterns
      - name: Check for sensitive patterns
        run: |
          if grep -rn "password\s*=\s*['\"]" --include="*.py" --include="*.js" --include="*.php" --include="*.java" .; then
            echo "ERROR: Potential hardcoded password found!"
            exit 1
          fi

      # Fixed: Verify no API keys in code
      - name: Check for API keys
        run: |
          if grep -rn "sk_live_\|AKIA" --include="*.py" --include="*.js" --include="*.php" --include="*.java" .; then
            echo "ERROR: API key found in source!"
            exit 1
          fi

CVE Examples

  • CVE-2022-25512: Team Awareness Kit included sensitive tokens in JavaScript source code accessible to users.
  • CVE-2022-24867: IT Asset Management tool exposed LDAP passwords in rendered HTML output.
  • CVE-2007-6197: Application leaked version numbers and hostnames in HTML comments.

References

  1. MITRE Corporation. "CWE-540: Inclusion of Sensitive Information in Source Code." https://cwe.mitre.org/data/definitions/540.html
  2. OWASP. "Secrets Management Cheat Sheet."
  3. GitHub. "About Secret Scanning."