Password in Configuration File

Description

Password in Configuration File is a vulnerability that occurs when a product stores passwords in configuration files that might be accessible to actors who should not know the password. Configuration files are often stored in predictable locations, may have insufficient access controls, and are frequently included in backups, version control systems, or deployment packages. Unlike hard-coded passwords in source code, configuration file passwords might seem like a legitimate storage mechanism, but they create significant security risks when the files are accessible to unauthorized users, included in logs, or exposed through application vulnerabilities. This weakness is particularly problematic when passwords are stored in plaintext rather than encrypted or hashed form.

Risk

Storing passwords in configuration files creates multiple attack vectors. Configuration files may be readable by other users on shared systems due to improper file permissions. Web applications may expose configuration files through directory traversal, local file inclusion, or misconfigured web servers. Version control systems often contain historical versions of configuration files with credentials. Backup systems may store configuration files in less-protected locations. Log aggregation systems may capture configuration file contents. Container images and deployment artifacts frequently include configuration files with embedded credentials. The risk is amplified because configuration files are often considered non-sensitive and may be shared between developers, included in support bundles, or accidentally committed to public repositories.

Solution

Avoid storing passwords directly in configuration files whenever possible. Use environment variables, secrets management systems, or encrypted credential stores instead. If passwords must be stored in configuration files, encrypt them using strong encryption with keys stored separately from the configuration. Use secure configuration management tools that handle credential encryption and access control. Set strict file permissions on configuration files (mode 600 or 640). Exclude configuration files containing credentials from version control using .gitignore or equivalent. Use configuration templates with placeholder values for sensitive data. Implement secrets scanning in CI/CD pipelines to detect accidentally committed credentials. Consider using external secrets managers like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

An attacker gaining access to the configuration file could read the stored password, enabling unauthorized access to the protected resource. They might also modify the password to lock out legitimate users or establish persistent access.

Example Code

Vulnerable Configuration (Multiple Formats)

The following examples demonstrate vulnerable password storage in configuration files:

# config.ini - Vulnerable: Plaintext passwords
[database]
host = localhost
port = 3306
username = admin
password = SuperSecretPassword123!

[api]
key = sk-live-abc123xyz456
secret = myApiSecretKey

[smtp]
username = [email protected]
password = EmailP@ssw0rd!
# application.yml - Vulnerable: Exposed credentials
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/myapp
    username: app_user
    password: DatabasePassword123!  # Exposed!

  mail:
    host: smtp.example.com
    username: [email protected]
    password: SmtpPassword456!  # Exposed!

aws:
  access-key-id: AKIAIOSFODNN7EXAMPLE
  secret-access-key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY  # Exposed!
<!-- web.xml - Vulnerable: Credentials in deployment descriptor -->
<context-param>
    <param-name>db.password</param-name>
    <param-value>ProductionDbPassword!</param-value>
</context-param>
// config.json - Vulnerable: All secrets in JSON config
{
    "database": {
        "password": "DbP@ssw0rd123"
    },
    "redis": {
        "password": "RedisSecret456"
    },
    "encryption": {
        "key": "MyEncryptionKey789"
    }
}
<?php
// config.php - Vulnerable: Credentials in PHP config
return [
    'db_password' => 'PhpDbPassword!',
    'api_key' => 'php-api-key-12345',
    'jwt_secret' => 'jwt-signing-secret-key'
];

Fixed Configuration (Multiple Approaches)

# application.yml - Fixed: References to external secrets
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/myapp
    username: ${DB_USERNAME}  # From environment variable
    password: ${DB_PASSWORD}  # From environment variable

  mail:
    host: smtp.example.com
    username: ${SMTP_USERNAME}
    password: ${SMTP_PASSWORD}

# Or reference secrets manager
secrets:
  provider: aws-secrets-manager
  database-credentials: myapp/database
  api-credentials: myapp/external-api
// SecureConfigLoader.java - Fixed: Secure credential loading
public class SecureConfigLoader {

    private final SecretsManager secretsManager;

    public DatabaseConfig loadDatabaseConfig() {
        // Load non-sensitive config from file
        Properties props = loadPropertiesFile("database.properties");

        // Load credentials from secrets manager
        Secret dbSecret = secretsManager.getSecret("myapp/database");

        return DatabaseConfig.builder()
            .host(props.getProperty("db.host"))
            .port(Integer.parseInt(props.getProperty("db.port")))
            .username(dbSecret.getString("username"))
            .password(dbSecret.getString("password"))
            .build();
    }

    public String getApiKey(String serviceName) {
        // Retrieve from secrets manager, never from config file
        return secretsManager.getSecret("myapp/api-keys/" + serviceName);
    }
}
# config_loader.py - Fixed: Encrypted credentials with separate key
import os
from cryptography.fernet import Fernet

class SecureConfig:
    def __init__(self):
        # Key from environment, not in config file
        key = os.environ.get('CONFIG_ENCRYPTION_KEY')
        self.cipher = Fernet(key.encode())

    def get_password(self, config_file, password_key):
        with open(config_file) as f:
            config = yaml.safe_load(f)

        encrypted_password = config.get(password_key)
        if encrypted_password:
            return self.cipher.decrypt(encrypted_password.encode()).decode()
        return None

# Usage with encrypted config
# config.yml contains: db_password: "gAAAAABf..."  (encrypted)
#!/bin/bash
# Fixed: Use environment variables or secrets manager

# Option 1: Environment variables (set securely, not in script)
export DB_PASSWORD="${DB_PASSWORD}"

# Option 2: Read from secrets manager
DB_PASSWORD=$(aws secretsmanager get-secret-value \
    --secret-id myapp/database \
    --query SecretString --output text | jq -r .password)

# Option 3: Read from HashiCorp Vault
DB_PASSWORD=$(vault kv get -field=password secret/myapp/database)
# Dockerfile - Fixed: Don't embed secrets in images
# BAD: ENV DB_PASSWORD=secret123

# GOOD: Secrets injected at runtime
# Use Docker secrets or environment injection
ENV DB_PASSWORD_FILE=/run/secrets/db_password

# Application reads from file or environment at runtime

The fixes use environment variables, external secrets managers, encrypted credentials with separate keys, or runtime secret injection instead of plaintext passwords in configuration files.


Exploited in the Wild

Jenkins Credential Exposure (Jenkins, 2022)

CVE-2022-38665 affected Jenkins, the popular CI/CD tool, where stored passwords in configuration files were accessible to users with read access to job configurations. The vulnerability exposed credentials used for connecting to external systems, potentially compromising entire deployment pipelines.

Laravel .env File Exposure (Multiple Organizations, Ongoing)

Thousands of Laravel applications have exposed their .env configuration files containing database passwords, API keys, and application secrets. Misconfigured web servers, exposed git repositories, and backup files have led to widespread credential exposure. Security researchers regularly find exposed Laravel credentials through simple search engine queries.

WordPress wp-config.php Exposure (WordPress Sites, Ongoing)

WordPress installations frequently expose wp-config.php files containing database credentials through misconfigured servers, backup files with predictable names (.bak, .old), or source code repository exposure. Attackers use these credentials for database access, content manipulation, and further system compromise.


Tools to Test/Exploit

  • Gitleaks — Scans git repositories and file systems for exposed passwords and secrets in configuration files.

  • TruffleHog — Deep credential scanning tool that detects secrets in configuration files and git history.

  • detect-secrets — Yelp's tool for detecting secrets in configuration files during development.


CVE Examples

  • CVE-2022-38665 — Jenkins stored unencrypted passwords in configuration files accessible to users with job read access.

  • CVE-2019-10352 — Application stored database credentials in plaintext configuration file accessible via web directory.

  • CVE-2021-21972 — VMware vCenter configuration files containing credentials exposed through file upload vulnerability.


References

  1. MITRE Corporation. "CWE-260: Password in Configuration File." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/260.html

  2. OWASP Foundation. "Credential Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Credential_Management_Cheat_Sheet.html

  3. HashiCorp. "Vault Documentation - Secret Management." https://www.vaultproject.io/docs

  4. AWS. "AWS Secrets Manager." https://aws.amazon.com/secrets-manager/