Cybersecurity

The 6 Aspects You Must Secure On Your MongoDB Instances

November 25th, 2020 | By Rui Trigo | 7 min read

Today, we present a MongoDB security manual with six security aspects to protect your MongoDB instances.

This is a complementary blog article after going through the adventure of deploying a high-availability MongoDB cluster on Docker and sharing it publicly.

You will learn a few details about MongoDB deployment vulnerabilities and security mechanisms. More importantly, learn how to protect your data with these features.

Objectives

  • Understand database aspects of security.

  • Find ways to implement authentication, authorization, and accounting (AAA).

  • Learn how to enable MongoDB's security features.

Prerequisites

Any running MongoDB instance to which you have full access will do.

Stand-alone or replica set, containerized or not.

We will also mention some details on MongoDB Docker instances. However, we will keep Docker-specific security tips for another post.

List of Quick Wins

Accessing data in a database has several stages.

We will explore these stages and find ways to harden them for a cumulative security effect.

Most of the time, each one of these stages blocks the next one (e.g., you need network access to get to the authentication part).

1. Network Access

MongoDB’s default port is 27017 (TCP). Choosing a different port to operate on might confuse some hackers, but it is still a minor security action because of port scanning, so you won't get that much out of it.

Assuming we choose the default port for our service, we will open that port on the database server's firewall. We do not wish to expose the traffic from this port to the internet.

There are two approaches to solving that, and both can be used simultaneously. One is limiting your traffic to your trusted servers through firewall configuration.

There’s a MongoDB feature you can use for this: IP Binding. You pass the --bind_ip argument on the MongoDB launch command to enable it.

Let's say your app1 server needs to access the MongoDB server for data. To limit traffic for that specific server, you start your server as:

mongod --bind_ip localhost,app1


If you are using Docker, you can avoid this risk by using a Docker network between your database and your client application.

You can add another layer of network security by creating a dedicated network segment for databases, in which you apply an ACL (access list) in the router and/or switch configuration.

2. System Access

The second A in AAA means authorization. We know privileged shell access is needed during database installation. When concluding the installation, locking system root user access is part of the drill.

Data analysts need to read database data, and applications also need to read and (almost always) write data as well.

As this can be addressed with database authentication (more on this in 4. Authorization), make sure to restrict root and other shell access to people who can't do their jobs without it. Only allow it for database and system administrators.

Furthermore, running MongoDB processes with a dedicated operating system user account is a good practice. Ensure that this account has permission to access data but no unnecessary permissions.

3. Authentication

Authentication is the first A in AAA. Authentication-wise, MongoDB supports 4 mechanisms:

  • SCRAM (default)

  • x.509 certificate authentication

  • LDAP proxy authentication

  • Kerberos authentication

If you are using MongoDB Enterprise Server, then you can benefit from LDAP and Kerberos support. Integrating your company identity and access management tool will make AAA 3rd A (Accounting) implementation easier, as every user will have a dedicated account associated with his records.

MongoDB has its own SCRAM implementations: SCRAM_SHA1 for versions below 4.0 and SCRAM_SHA256 for 4.0 and above.

You can think of SHA-256 as the successor of SHA-1, so pick the latter if it's available in your database version.

Replica set key files also use the SCRAM authentication mechanism, where these key files contain the shared password between the replica set members.

Another internal authentication mechanism supported in replica sets is x.509. You can read more about replica sets and how to generate key files.

To be able to use the X.509 certificate authentication mechanism, there are some requirements regarding certificate attributes.

To enable x.509 authentication, add --tlsMode, --tlsCertificateKeyFile, and --tlsCAFile (in case the certificate has a certificate authority). To perform remote connections to the database, specify --bind_ip.

mongod --tlsMode requireTLS --tlsCertificateKeyFile <path to TLS/SSL certificate and key PEM file> --tlsCAFile <path to root CA PEM file> --bind_ip <hostnames>


To generate these certificates, you can use the OpenSSL library on Linux or the equivalent on other operating systems.

openssl x509 -in <pathToClientPEM> -inform PEM -subject -nameopt RFC2253


The command returns the subject string as well as the certificate.

subject= CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry
-----BEGIN CERTIFICATE-----
# ...
-----END CERTIFICATE-----


Next, add a user to the $external database using the obtained subject string, like in the example below:

db.getSiblingDB("$external").runCommand(
  {
    createUser: "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry",
    roles: [
         { role: "readWrite", db: "test" },
         { role: "userAdminAnyDatabase", db: "admin" }
    ],
    writeConcern: { w: "majority" , wtimeout: 5000 }
  }
)



Finally, connect to the database with the arguments for TLS, certificate location, CA file location, authentication database, and authentication mechanism.

mongo --tls --tlsCertificateKeyFile <path to client PEM file> --tlsCAFile <path to root CA PEM file>  --authenticationDatabase '$external' --authenticationMechanism MONGODB-X509


You have now successfully connected to your database using the X.509 authentication mechanism.

4. Authorization

For non-testing environments (like production), it is not recommended to have Access Control disabled, as this grants all privileges to any successful access to the database. To enable authentication, follow the procedure below.

# start MongoDB without access control
mongod
# connect to the instance
mongo
// create the user administrator
use admin
db.createUser(
  {
    user: "myUserAdmin",
    pwd: passwordPrompt(), // or cleartext password
    roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ]
  }
)
// shutdown mongod instance
db.adminCommand( { shutdown: 1 } )
# start MongoDB with access control
mongo --auth


If you're using MongoDB on Docker, you can create an administrator through the MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD environment variables (-e argument). Like so:

docker run -d -e MONGO_INITDB_ROOT_USERNAME=<username> -e MONGO_INITDB_ROOT_PASSWORD=<password> mongo:4.4


Do not neglect human usability and convenience. Make sure all passwords are strong, fit your company's password policy, and are stored securely.

MongoDB has a set of built-in roles and allows us to create new ones. Use roles to help when giving privileges while applying the principle of least privilege to user accounts and avoiding user account abuse.

5. Encrypted connections

Let's now see how to configure encrypted connections to protect you from sniffing attacks.

If you think about internet browsers, you notice how they keep pressing for users to navigate on sites that support HTTP over TLS, also known as HTTPS.

That enforcement exists for a reason: sensitive data protection, both for the client and the server. TLS is therefore protecting this sensitive data during client-server communication, bi-directionally.

We have explained how to use TLS certificates on 4. Authentication, and now we will see how to encrypt our communications between the database server and a client app through TLS configuration on the application’s MongoDB driver.

First, to configure the MongoDB server to require our TLS certificate, add the --tlsMode and --tlsCertificateKeyFile arguments:

mongod --tlsMode requireTLS --tlsCertificateKeyFile <pem>


To test the connection to the Mongo shell, type in:

mongo --tls --host <hostname.example.com> --tlsCertificateKeyFile <certificate_key_location>


Then, add TLS options to the database connection in your application code.

Here is a snippet of a NodeJS application using MongoDB’s official driver package. You can find more of these encryption options in the driver documentation.

const MongoClient = require('mongodb').MongoClient;
const fs = require('fs');

// Read the certificate authority
const ca = [fs.readFileSync(__dirname + "/ssl/ca.pem")];

const client = new MongoClient('mongodb://localhost:27017?ssl=true', {
  sslValidate:true,
  sslCA:ca
});

// Connect validating the returned certificates from the server
client.connect(function(err) {
  client.close();
});


6. Encryption at rest

MongoDB Enterprise Server comes with an Encryption at Rest feature.

Through a master and database key system, this allows us to store our data in an encrypted state by configuring the field as encrypted on rest.

You can learn more about the supported standards and enciphering/deciphering keys on the MongoDB documentation.

On the other side, if you will stick with the MongoDB Community, on v4.2 MongoDB started supporting Client-Side Field Level Encryption.

Here’s how it works: you generate the necessary keys and load them in your database driver (e.g. NodeJS MongoDB driver). Then, you will be able to encrypt your data before storing it in the database and decrypt it for your application to read it.

Below, you can find a JavaScript code snippet showing data encryption and decryption happening on MongoDB’s NodeJS driver with the help of the npm package mongodb-client-encryption.

const unencryptedClient = new MongoClient(URL, { useUnifiedTopology: true });
  try {
	await unencryptedClient.connect();
	const clientEncryption = new ClientEncryption(unencryptedClient, { kmsProviders, keyVaultNamespace });

	async function encryptMyData(value) {
  	const keyId = await clientEncryption.createDataKey('local');
  	console.log("keyId", keyId);
  	return clientEncryption.encrypt(value, { keyId, algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' });
	}

	async function decryptMyValue(value) {
  	return clientEncryption.decrypt(value);
	}

	const data2 = await encryptMyData("sensitive_data");
	const mKey = key + 1;
	const collection = unencryptedClient.db("test").collection('coll');
	await collection.insertOne({ name: data2, key: mKey });
	const a = await collection.findOne({ key: mKey });
	console.log("encrypted:", a.name);
	const decrypteddata = await decryptMyValue(a.name);
	console.log("decrypted:", decrypteddata);

  } finally {
	await unencryptedClient.close();
  }


Conclusion

While this post attempts to cover some of the most important quick wins you can achieve to secure your MongoDB instances, there is much more to MongoDB security.

Upgrading database and driver versions frequently, connecting a monitoring tool, and keeping track of database access and configuration are also good ideas to increase security.

Nevertheless, even if the system was theoretically entirely secured, it is always prone to human mistakes. Make sure the people working with you are conscious of the importance of keeping data secured - properly securing a system is always contingent on all users taking security seriously.

Security is everyone's job. Like in tandem kayaks, it only makes sense if everyone is paddling together in the same direction, with all efforts contributing to the same purpose.

Lastly, although this post has focused on database security, it’s also advisable that you protect the JavaScript source code of your web and mobile apps. See our tutorials on protecting React, Angular, Vue, React Native, Ionic, and NativeScript.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Web Development

MongoDB Native Driver vs Mongoose: Performance Benchmarks

Mongoose brings several useful features in Node, but how does it perform compared to the MongoDB native driver? Let's benchmark both using Express!

December 17, 2020 | By Camilo Reyes | 5 min read

Web Development

How We Achieved MongoDB Replication on Docker

Database disaster recovery processes can be hard to manage. Let's explore how we used MongoDB replication on Docker to lower the odds of disaster scenarios.

April 21, 2020 | By Rui Trigo | 6 min read

Section Divider

Subscribe to Our Newsletter