Skip to main content

Common Misconfiguration

Hardcoded database passwords in configuration files, connection strings, and migration scripts expose databases to unauthorized access.

Vulnerable Example

# VULNERABLE - Django settings.py with hardcoded password
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'production_db',
        'USER': 'postgres',
        'PASSWORD': 'PostgresAdmin123!', # Never hardcode!
        'HOST': 'db.production.com',
        'PORT': '5432',
    },
    'analytics': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'analytics',
        'USER': 'root',
        'PASSWORD': 'MyS3cr3tP@ssw0rd', # Never hardcode!
        'HOST': 'mysql.production.com',
        'PORT': '3306',
    }
}

# Cache password
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://default:RedisPassword123@redis.production.com:6379/1',
    }
}
// VULNERABLE - Node.js database configuration
module.exports = {
    development: {
        username: 'dev_user',
        password: 'DevPass123',
        database: 'dev_db',
        host: '127.0.0.1',
        dialect: 'postgres'
    },
    production: {
        username: 'prod_user',
        password: 'ProdPassword2024!', // Never hardcode!
        database: 'production_db',
        host: 'db.production.com',
        dialect: 'postgres',
        dialectOptions: {
            ssl: {
                require: true,
                rejectUnauthorized: false
            }
        }
    },
    mongodb: {
        uri: 'mongodb://admin:MongoAdmin123!@[mongo.production.com:27017/myapp](https://mongo.production.com:27017/myapp)'
    },
    redis: {
        host: 'redis.production.com',
        port: 6379,
        password: 'RedisSecretPass!' // Never hardcode!
    }
};
# VULNERABLE - Rails database.yml
production:
  adapter: postgresql
  encoding: unicode
  database: myapp_production
  username: myapp
  password: Rails2024Prod! # Never hardcode!
  host: postgresql.production.com
  port: 5432
  pool: 25

redis:
  url: redis://default:RedisPass123@redis.production.com:6379/0

Secure Example

# SECURE - Django settings with environment variables
import os
from urllib.parse import urlparse
import dj_database_url

# Use environment variable or connection string
DATABASE_URL = os.environ.get('DATABASE_URL')

if DATABASE_URL:
    DATABASES = {
        'default': dj_database_url.parse(DATABASE_URL, conn_max_age=600)
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': os.environ.get('DB_NAME', 'myapp'),
            'USER': os.environ.get('DB_USER', 'postgres'),
            'PASSWORD': os.environ.get('DB_PASSWORD'), # Required from environment
            'HOST': os.environ.get('DB_HOST', 'localhost'),
            'PORT': os.environ.get('DB_PORT', '5432'),
            'CONN_MAX_AGE': 600,
            'OPTIONS': {
                'sslmode': 'require' if os.environ.get('DB_SSL') == 'true' else 'prefer',
                'connect_timeout': 10,
            }
        }
    }

# Validate database configuration
if not DATABASES['default'].get('PASSWORD'):
    raise ValueError("Database password not configured. Set DB_PASSWORD environment variable.")

# Redis with password from environment
REDIS_URL = os.environ.get('REDIS_URL')
if REDIS_URL:
    redis_url = urlparse(REDIS_URL)
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.redis.RedisCache',
            'LOCATION': REDIS_URL,
            'OPTIONS': {
                'CONNECTION_POOL_KWARGS': {
                    'max_connections': 50,
                    'retry_on_timeout': True,
                    'socket_keepalive': True,
                    'socket_keepalive_options': {
                        1: 1, # TCP_KEEPIDLE
                        2: 1, # TCP_KEEPINTVL
                        3: 5, # TCP_KEEPCNT
                    }
                }
            }
        }
    }
// SECURE - Node.js with secure password management
const { Sequelize } = require('sequelize');
const { createClient } = require('redis');
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");

class SecureDatabaseConfig {
    constructor() {
        this.connections = {};
    }

    async initializeFromSecretsManager() {
        const client = new SecretsManagerClient({ region: process.env.AWS_REGION });

        try {
            const command = new GetSecretValueCommand({
                SecretId: "prod/database/credentials",
            });

            const response = await client.send(command);
            const secrets = JSON.parse(response.SecretString);

            return {
                username: secrets.username,
                password: secrets.password,
                engine: secrets.engine,
                host: secrets.host,
                port: secrets.port,
                dbname: secrets.dbname
            };
        } catch (error) {
            console.error("Failed to retrieve database credentials:", error);
            throw error;
        }
    }

    async createPostgresConnection() {
        const config = await this.initializeFromSecretsManager();

        const sequelize = new Sequelize({
            dialect: 'postgres',
            host: config.host,
            port: config.port,
            database: config.dbname,
            username: config.username,
            password: config.password,
            logging: process.env.NODE_ENV === 'development',
            pool: {
                max: 20,
                min: 5,
                acquire: 30000,
                idle: 10000
            },
            dialectOptions: {
                ssl: {
                    require: true,
                    rejectUnauthorized: process.env.NODE_ENV === 'production'
                },
                keepAlive: true,
                statement_timeout: 30000,
                idle_in_transaction_session_timeout: 60000
            },
            retry: {
                max: 3,
                match: [
                    Sequelize.ConnectionError,
                    Sequelize.ConnectionTimedOutError,
                    Sequelize.TimeoutError
                ]
            }
        });

        // Test connection
        await sequelize.authenticate();
        console.log('Database connection established successfully.');

        this.connections.postgres = sequelize;
        return sequelize;
    }

    async createRedisConnection() {
        // Use environment variable for AUTH token
        const redis = createClient({
            socket: {
                host: process.env.REDIS_HOST,
                port: parseInt(process.env.REDIS_PORT) || 6379,
                tls: process.env.NODE_ENV === 'production'
            },
            password: process.env.REDIS_AUTH_TOKEN, // Use AUTH token
            database: parseInt(process.env.REDIS_DB) || 0,
            reconnectStrategy: (options) => { // Use 'reconnectStrategy' for node-redis v4
                if (options.error && options.error.code === 'ECONNREFUSED') {
                    return new Error('Redis connection refused');
                }
                if (options.total_retry_time > 1000 * 60 * 60) {
                    return new Error('Redis retry time exhausted');
                }
                if (options.attempt > 10) {
                    return undefined;
                }
                return Math.min(options.attempt * 100, 3000);
            }
        });

        redis.on('error', (err) => console.error('Redis Client Error', err));
        await redis.connect();

        this.connections.redis = redis;
        return redis;
    }

    // Password rotation handler
    async rotatePasswords() {
        console.log('Starting password rotation...');

        // Close existing connections
        for (const [name, conn] of Object.entries(this.connections)) {
            if (conn.close) await conn.close();
            if (conn.quit) await conn.quit();
        }

        // Reinitialize with new credentials
        await this.createPostgresConnection();
        await this.createRedisConnection();

        console.log('Password rotation completed successfully');
    }
}
# SECURE - Kubernetes secret for database passwords
apiVersion: v1
kind: Secret
metadata:
  name: database-passwords
  namespace: production
type: Opaque
stringData:
  postgres-password: ${POSTGRES_PASSWORD} # Value is injected by CI/CD
  mysql-password: ${MYSQL_PASSWORD}
  redis-password: ${REDIS_PASSWORD}
  mongodb-password: ${MONGODB_PASSWORD}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: database-config
data:
  postgres-host: "postgresql.production.svc.cluster.local"
  postgres-port: "5432"
  postgres-database: "myapp"
  postgres-user: "appuser"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  template:
    spec:
      containers:
      - name: app
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: database-passwords
              key: postgres-password
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: database-config
              key: postgres-host

Detection Patterns

  • Key/Value Pair: `(password|passwd|pwd|secret|token)['"]?\s*[:=]\s*['"][^'"]+['"]`
  • Connection String: `(mysql|postgres|redis|mongodb)://[^:]+:[^@]+@`
  • Django Password: `'PASSWORD':\s*['"][^'"]+['"]`
  • Rails Password: `password:\s*[^#\s]+`

Prevention Best Practices

  1. Use Environment Variables: Never hardcode passwords. Load them from the environment at runtime.
  2. Implement Secrets Management: Use a dedicated secrets manager (like HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or GCP Secret Manager) to store and inject credentials.
  3. Use IAM Database Authentication: Where possible (e.g., AWS RDS, GCP Cloud SQL), use IAM roles to authenticate to the database without any passwords.
  4. Implement Password Rotation: Enforce and automate regular password rotation policies.
  5. Use Strong Passwords: Ensure all database passwords are long, complex, and randomly generated.
  6. Enable SSL/TLS: Encrypt all database connections in transit.
  7. Use Connection Pooling: Implement secure connection pooling to manage connections.
  8. Monitor Failed Logins: Actively monitor and alert on failed authentication attempts.
  9. Use Separate Credentials: Use different passwords and service accounts for each environment (dev, staging, prod).
  10. Implement Audit Logging: Enable database audit logging to track all access and changes.