Skip to main content

Common Misconfiguration

Exposed NoSQL database credentials can lead to data breaches, ransomware attacks, and complete database compromise.

Vulnerable Example

// VULNERABLE - Hardcoded MongoDB credentials
const mongoose = require('mongoose');
const redis = require('redis');
const { Client } = require('@elastic/elasticsearch');

// MongoDB with credentials in URI
const MONGO_URI = 'mongodb://admin:MySecretPass123!@mongodb.production.com:27017/myapp?authSource=admin';

// MongoDB with separate credentials
const mongoConfig = {
    host: 'cluster0.mongodb.net',
    username: 'dbAdmin',
    password: 'MongoDB@Pass2024!',
    database: 'production',
    authSource: 'admin'
};

// Redis with password
const redisClient = redis.createClient({
    host: 'redis.production.com',
    port: 6379,
    password: 'RedisPass123!@#',
    db: 0
});

// Elasticsearch with API key
const elasticClient = new Client({
    node: 'https://elastic.production.com:9200',
    auth: {
        username: 'elastic',
        password: 'ElasticSearch2024!',
        apiKey: 'VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=='
    }
});

// Cassandra credentials
const cassandraAuth = {
    username: 'cassandra',
    password: 'cassandra123',
    keyspace: 'production_ks'
};

mongoose.connect(MONGO_URI);

Secure Example

// SECURE - Using environment variables and connection management
const mongoose = require('mongoose');
const redis = require('redis');
const { Client } = require('@elastic/elasticsearch');
const { MongoClient } = require('mongodb');

class SecureNoSQLConnections {
    constructor() {
        this.connections = {};
    }
    
    async connectMongoDB() {
        // Build connection string from environment
        const config = {
            username: process.env.MONGODB_USERNAME,
            password: process.env.MONGODB_PASSWORD,
            host: process.env.MONGODB_HOST,
            port: process.env.MONGODB_PORT || 27017,
            database: process.env.MONGODB_DATABASE,
            authSource: process.env.MONGODB_AUTH_SOURCE || 'admin'
        };
        
        // Validate required fields
        if (!config.username || !config.password || !config.host) {
            throw new Error('MongoDB configuration incomplete');
        }
        
        // Build URI with proper encoding
        const uri = this.buildMongoUri(config);
        
        // Connection options for production
        const options = {
            useNewUrlParser: true,
            useUnifiedTopology: true,
            serverSelectionTimeoutMS: 5000,
            socketTimeoutMS: 45000,
            maxPoolSize: 10,
            minPoolSize: 2,
            ssl: process.env.NODE_ENV === 'production',
            sslValidate: true,
            sslCA: process.env.MONGODB_CA_CERT,
            authMechanism: 'SCRAM-SHA-256',
            retryWrites: true,
            w: 'majority'
        };
        
        try {
            const client = new MongoClient(uri, options);
            await client.connect();
            
            // Verify connection
            await client.db().admin().ping();
            
            this.connections.mongodb = client;
            console.log('MongoDB connected securely');
            
            return client;
        } catch (error) {
            console.error('MongoDB connection failed:', error.message);
            throw error;
        }
    }
    
    buildMongoUri(config) {
        const credentials = encodeURIComponent(config.username) + ':' + 
                          encodeURIComponent(config.password);
        
        // Support replica sets
        const hosts = config.host.includes(',') 
            ? config.host 
            : `${config.host}:${config.port}`;
        
        let uri = `mongodb://${credentials}@${hosts}/${config.database}`;
        
        // Add query parameters
        const params = [];
        if (config.authSource) params.push(`authSource=${config.authSource}`);
        if (process.env.MONGODB_REPLICA_SET) {
            params.push(`replicaSet=${process.env.MONGODB_REPLICA_SET}`);
        }
        
        if (params.length > 0) {
            uri += '?' + params.join('&');
        }
        
        return uri;
    }
    
    async connectRedis() {
        const redisOptions = {
            socket: {
                host: process.env.REDIS_HOST,
                port: parseInt(process.env.REDIS_PORT) || 6379,
                tls: process.env.NODE_ENV === 'production',
                rejectUnauthorized: true
            },
            password: process.env.REDIS_PASSWORD,
            database: parseInt(process.env.REDIS_DB) || 0,
            lazyConnect: true,
            reconnectStrategy: (retries) => {
                if (retries > 10) {
                    return new Error('Redis reconnection attempts exhausted');
                }
                return Math.min(retries * 100, 3000);
            }
        };
        
        // Use Redis Sentinel for HA
        if (process.env.REDIS_SENTINELS) {
            redisOptions.sentinels = JSON.parse(process.env.REDIS_SENTINELS);
            redisOptions.name = process.env.REDIS_MASTER_NAME;
        }
        
        const client = redis.createClient(redisOptions);
        
        // Error handling
        client.on('error', (err) => {
            console.error('Redis error:', err);
        });
        
        await client.connect();
        
        // Test connection
        await client.ping();
        
        this.connections.redis = client;
        console.log('Redis connected securely');
        
        return client;
    }
    
    async connectElasticsearch() {
        // Use Cloud ID for Elastic Cloud
        const config = process.env.ELASTIC_CLOUD_ID ? {
            cloud: {
                id: process.env.ELASTIC_CLOUD_ID
            },
            auth: {
                apiKey: process.env.ELASTIC_API_KEY
            }
        } : {
            node: process.env.ELASTICSEARCH_URL,
            auth: {
                username: process.env.ELASTIC_USERNAME,
                password: process.env.ELASTIC_PASSWORD
            },
            ssl: {
                rejectUnauthorized: true,
                ca: process.env.ELASTIC_CA_CERT
            }
        };
        
        const client = new Client(config);
        
        // Test connection
        const info = await client.info();
        console.log('Elasticsearch connected:', info.body.cluster_name);
        
        this.connections.elasticsearch = client;
        return client;
    }
    
    async closeAll() {
        for (const [name, connection] of Object.entries(this.connections)) {
            try {
                if (connection.close) {
                    await connection.close();
                } else if (connection.quit) {
                    await connection.quit();
                }
                console.log(`${name} connection closed`);
            } catch (error) {
                console.error(`Error closing ${name}:`, error);
            }
        }
    }
}

// Kubernetes Secret example
const k8sSecret = `
apiVersion: v1
kind: Secret
metadata:
  name: nosql-credentials
type: Opaque
stringData:
  mongodb-uri: mongodb://user:pass@mongo:27017/db
  redis-password: your-redis-password
  elastic-api-key: your-elastic-api-key
`;

Detection Patterns

  • MongoDB URI: mongodb(\+srv)?://[^:]+:[^@]+@
  • Redis URL: redis://[^:]*:[^@]+@
  • Elasticsearch: https?://[^:]+:[^@]+@.*:9200
  • CouchDB: https?://[^:]+:[^@]+@.*:5984

Prevention Best Practices

  1. Use Secrets Management: Never hardcode credentials. Load them from environment variables or a secrets manager (like HashiCorp Vault, AWS Secrets Manager, etc.) at runtime.
  2. Implement Connection Pooling: Use a connection pool to efficiently manage and reuse database connections, reducing the overhead of repeated authentication.
  3. Use SSL/TLS: Encrypt all data in transit between your application and the database to prevent sniffing.
  4. Enforce Strong Authentication: Don’t run with authentication disabled. Use modern, strong mechanisms like SCRAM-SHA-256 for MongoDB.
  5. Implement IP Whitelisting: Configure your database (or its firewall/security group) to only accept connections from your application’s specific IP addresses.
  6. Use Least Privilege: Create dedicated database users for your application that only have the permissions they need (e.g., readWrite, not dbAdmin).
  7. Monitor Access: Log and alert on failed login attempts and connections from unexpected sources.
  8. Implement High Availability: Use features like replica sets (MongoDB) or Sentinel (Redis) to ensure your database is resilient to failure.
  9. Audit and Rotate Credentials: Regularly audit who has access and automatically rotate all passwords and API keys.