Inclusion of Sensitive Information in an Include File
Description
Inclusion of Sensitive Information in an Include File is a vulnerability where sensitive data is stored in files designed to be included by other source files, but these include files may become directly accessible to attackers. Include files (such as .inc, .conf, or header files) often contain database credentials, API keys, configuration settings, and other sensitive information. When web servers are misconfigured to serve these files as plain text rather than processing them through the appropriate interpreter, or when these files are placed in publicly accessible directories, attackers can retrieve them and extract the embedded sensitive data.
Risk
Include files with sensitive information create critical security exposures. Files with extensions like .inc may not be recognized by web servers as code and are served as plain text, revealing their contents. Configuration include files often contain database credentials, allowing attackers to directly access databases. API keys in include files enable unauthorized service access. Hardcoded passwords bypass normal authentication entirely. Even if the include files are not directly accessible, source code leaks, backup files, or version control exposure can reveal their contents. The centralized nature of include files means one exposed file often compromises multiple system components.
Solution
Never store sensitive information directly in include files. Use environment variables or secure configuration management systems for credentials. Place include files outside the web-accessible document root. Configure web servers to deny access to include file extensions (.inc, .conf, .ini). Use PHP, ASP, or other server-side extensions for include files so they are processed rather than served as text. Implement proper access controls on configuration directories. Use secrets management solutions like HashiCorp Vault for sensitive configuration. Regularly audit include files for sensitive data exposure.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Application Data - Attackers can access sensitive information embedded in include files, including credentials, API keys, database connection strings, and system configuration details. |
Example Code
Vulnerable Code
<?php
// Vulnerable: database.inc with credentials (accessible as plain text)
// File: /var/www/html/includes/database.inc
// Vulnerable: Credentials in include file
$dbName = 'production_users';
$dbHost = 'db.company.internal';
$dbUser = 'app_admin';
$dbPassword = 'Pr0duct1on_P@ssw0rd!';
// Vulnerable: API keys in include file
$stripeKey = 'sk_live_51ABC123XYZ789';
$awsAccessKey = 'AKIAIOSFODNN7EXAMPLE';
$awsSecretKey = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY';
// If .inc extension is not handled by PHP, this file is served as plain text
// Request: GET /includes/database.inc
// Response: All credentials visible!
?>
<?php
// Vulnerable: config.inc.php that's still accessible
// File: /var/www/html/includes/config.inc.php
// Vulnerable: Even with .php extension, may be exposed through:
// - Directory listing
// - Backup files (config.inc.php.bak)
// - Version control exposure (.git)
define('DB_HOST', 'mysql.company.com');
define('DB_USER', 'root');
define('DB_PASS', 'RootPassword123!');
define('DB_NAME', 'customer_data');
// Vulnerable: Encryption keys
define('ENCRYPTION_KEY', '32-character-encryption-key-here');
define('JWT_SECRET', 'jwt-signing-secret-2024');
// Vulnerable: Third-party service credentials
define('SMTP_PASSWORD', 'mail_server_password');
define('PAYMENT_API_SECRET', 'payment_processor_secret');
?>
// Vulnerable: C header file with hardcoded credentials
// File: config.h (may be in public repository or exposed)
#ifndef CONFIG_H
#define CONFIG_H
// Vulnerable: Database credentials in header
#define DB_HOST "192.168.1.100"
#define DB_PORT 3306
#define DB_USER "admin"
#define DB_PASSWORD "AdminPassword123"
#define DB_NAME "production"
// Vulnerable: API keys
#define API_KEY "sk_live_production_key_12345"
#define API_SECRET "api_secret_value_67890"
// Vulnerable: Encryption parameters
#define ENCRYPTION_KEY "AES256EncryptionKeyValue"
#define ENCRYPTION_IV "InitializationVector"
#endif
<!-- Vulnerable: ASP include file with credentials -->
<!-- File: /includes/connection.inc -->
<%
' Vulnerable: Database connection string with credentials
Dim connectionString
connectionString = "Provider=SQLOLEDB;Data Source=sql.company.com;" & _
"Initial Catalog=Production;User ID=sa;Password=SysAdmin123!"
' Vulnerable: LDAP credentials
Dim ldapUser, ldapPassword
ldapUser = "cn=admin,dc=company,dc=com"
ldapPassword = "LDAPAdminPassword"
' Vulnerable: Service account credentials
Dim serviceAccount, servicePassword
serviceAccount = "DOMAIN\ServiceAccount"
servicePassword = "Service@ccount123"
%>
# Vulnerable: Python config include with secrets
# File: config/settings.inc.py (may be served by misconfigured server)
# Vulnerable: Database settings
DATABASE = {
'host': 'db.company.internal',
'port': 5432,
'name': 'production',
'user': 'postgres',
'password': 'PostgresPassword123!'
}
# Vulnerable: Secret keys
SECRET_KEY = 'django-insecure-very-secret-key-here'
API_KEY = 'production-api-key-value'
# Vulnerable: Third-party credentials
AWS_ACCESS_KEY = 'AKIAIOSFODNN7EXAMPLE'
AWS_SECRET_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
STRIPE_SECRET = 'sk_live_production_stripe_key'
Fixed Code
<?php
// Fixed: Database configuration using environment variables
// File: /var/www/html/includes/database.php
// Fixed: No credentials in file, load from environment
function getDatabaseConfig() {
return [
'host' => getenv('DB_HOST'),
'name' => getenv('DB_NAME'),
'user' => getenv('DB_USER'),
'password' => getenv('DB_PASSWORD')
];
}
// Fixed: Validate configuration is present
function validateDatabaseConfig($config) {
foreach (['host', 'name', 'user', 'password'] as $key) {
if (empty($config[$key])) {
error_log("Database configuration missing: $key");
throw new Exception("Configuration error");
}
}
return $config;
}
// Fixed: API keys from environment
function getApiKey($service) {
$key = getenv(strtoupper($service) . '_API_KEY');
if (empty($key)) {
throw new Exception("API key not configured for: $service");
}
return $key;
}
?>
<?php
// Fixed: Secure config loader
// File: /var/www/app/config/config.php (outside webroot)
class SecureConfig {
private static $instance = null;
private $config = [];
private function __construct() {
// Fixed: Load from environment variables
$this->loadFromEnvironment();
// Fixed: Or load from secure file outside webroot
$this->loadFromSecureFile();
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
private function loadFromEnvironment() {
$this->config['db'] = [
'host' => getenv('DB_HOST'),
'user' => getenv('DB_USER'),
'password' => getenv('DB_PASSWORD'),
'name' => getenv('DB_NAME')
];
$this->config['encryption_key'] = getenv('ENCRYPTION_KEY');
$this->config['jwt_secret'] = getenv('JWT_SECRET');
}
private function loadFromSecureFile() {
// Fixed: Config file outside webroot
$configFile = '/etc/app/secrets.json';
if (file_exists($configFile)) {
$data = json_decode(file_get_contents($configFile), true);
if ($data) {
$this->config = array_merge($this->config, $data);
}
}
}
public function get($key, $default = null) {
return $this->config[$key] ?? $default;
}
}
?>
# Fixed: Apache configuration to block include files
# /etc/apache2/sites-available/example.conf
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/html
# Fixed: Deny access to include file extensions
<FilesMatch "\.(inc|conf|ini|cfg|bak|old)$">
Require all denied
</FilesMatch>
# Fixed: Deny access to config directories
<DirectoryMatch "/(config|includes|conf)/">
Require all denied
</DirectoryMatch>
# Fixed: Block backup files
<FilesMatch "(~|\.bak|\.old|\.orig|\.swp)$">
Require all denied
</FilesMatch>
</VirtualHost>
// Fixed: C configuration loaded from secure source
// File: config.h
#ifndef CONFIG_H
#define CONFIG_H
// Fixed: No hardcoded credentials, only structure definitions
typedef struct {
char host[256];
int port;
char user[64];
char password[128];
char database[64];
} DatabaseConfig;
typedef struct {
char api_key[256];
char api_secret[256];
} ApiConfig;
// Fixed: Functions to load configuration securely
int load_database_config(DatabaseConfig* config);
int load_api_config(ApiConfig* config);
// Fixed: Configuration loaded from environment or secure file
// Implementation in config.c, not exposed in header
#endif
// Fixed: config.c - secure configuration loading
#include "config.h"
#include <stdlib.h>
#include <string.h>
int load_database_config(DatabaseConfig* config) {
// Fixed: Load from environment variables
const char* host = getenv("DB_HOST");
const char* user = getenv("DB_USER");
const char* password = getenv("DB_PASSWORD");
const char* database = getenv("DB_NAME");
const char* port_str = getenv("DB_PORT");
if (!host || !user || !password || !database) {
return -1; // Configuration missing
}
strncpy(config->host, host, sizeof(config->host) - 1);
strncpy(config->user, user, sizeof(config->user) - 1);
strncpy(config->password, password, sizeof(config->password) - 1);
strncpy(config->database, database, sizeof(config->database) - 1);
config->port = port_str ? atoi(port_str) : 3306;
return 0;
}
# Fixed: Python secure configuration
# File: config/settings.py
import os
from functools import lru_cache
class SecureConfig:
"""Configuration loaded from environment, never hardcoded."""
@staticmethod
@lru_cache()
def get_database_config():
"""Load database configuration from environment."""
config = {
'host': os.environ.get('DB_HOST'),
'port': int(os.environ.get('DB_PORT', 5432)),
'name': os.environ.get('DB_NAME'),
'user': os.environ.get('DB_USER'),
'password': os.environ.get('DB_PASSWORD')
}
# Fixed: Validate required fields
required = ['host', 'name', 'user', 'password']
missing = [k for k in required if not config.get(k)]
if missing:
raise ValueError(f"Missing database configuration: {missing}")
return config
@staticmethod
def get_secret_key():
"""Get application secret key from environment."""
key = os.environ.get('SECRET_KEY')
if not key:
raise ValueError("SECRET_KEY environment variable required")
return key
@staticmethod
def get_api_key(service):
"""Get API key for a service from environment."""
key_name = f'{service.upper()}_API_KEY'
key = os.environ.get(key_name)
if not key:
raise ValueError(f"API key not configured: {key_name}")
return key
# Fixed: Usage
# database_config = SecureConfig.get_database_config()
# stripe_key = SecureConfig.get_api_key('stripe')
# Fixed: Docker Compose with secrets management
version: '3.8'
services:
app:
build: .
environment:
- DB_HOST=db
- DB_NAME=production
- DB_USER_FILE=/run/secrets/db_user
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_user
- db_password
- api_key
secrets:
db_user:
external: true
db_password:
external: true
api_key:
external: true
CVE Examples
- CVE-2018-1000130: Jolokia contained sensitive information in an include file that could be exposed.
- CVE-2016-9566: Nagios XI included sensitive database credentials in a configuration include file.
- CVE-2012-0867: PostgreSQL exposed credentials through include file misconfiguration.
References
- MITRE Corporation. "CWE-541: Inclusion of Sensitive Information in an Include File." https://cwe.mitre.org/data/definitions/541.html
- OWASP. "Secrets Management Cheat Sheet."
- OWASP. "OWASP Top Ten 2021 - A05:2021 Security Misconfiguration."