MongoDB introduces new capabilities that change the way micro-services interact with the database, capabilities that are either absent or exist only partially in high-end commercial databases such as Oracle. In this session I will share from my experiences building a cloud-based, multi-tenant SaaS application with extreme security requirements. We will cover topics including considerations for storing multi-tenant data in the database, best practices for authentication and authorization, and performance considerations specific to security in MongoDB.
First a little bit about myself, some numbers and data about me, they all true and tell something, after all I’ve been with data and databases my entire life…….
Data velocity is moderate not high...
Agile – there is no other way!
I’m not a guy that is afraid of complex databases but
Application enable optimistic locking, no need for database (pessimistic) locks
No updates, always inserts with versions
Incidents... We used to be all about resiliency, stability - but so many things have happened, so many incidents – security is a must...
Threats are there. Things will go wrong. These are mere examples…
Analyze the perpetual trade off between performance and security
One leaked password would compromise data of one tenant and not the entire data set, as data is really isolated.
One impersonation will expose 1 tenant
One bug of a developer, will cause damage to one tenant
Hardeninig?
We are a multi tenant application, there is an opportunity to enjoy good economics and share resources, but we need to maintain security, which is better with isolation
The x.509 client authentication allows clients to authenticate to servers with certificates rather than with a username and password.
Rest: If I, Cisco, was reckless and lost the drive, the thief will have to work very hard to decrypt one tenant’s data! Others are completely isolated and protected
A database is a file in the filesystem by default
From mongo docs:
Use this option in conjunction with your file system and device configuration so that MongoDB will store data on a number of distinct disk devices to increase write throughput or disk capacity.
Flight: new in 2.6
So this means I need to connect with a diff cert for every user…..
sslMode = <disabled|allowSSL|preferSSL|requireSSL>
In other words, this put the sole security responsibility on application server, and made the database completely blind.
That way, it was possible to create a pool of connections authenticated by a generic "appserver" but now this generic user has no data access privileges! Only privileges it had is to other users such as ”Foo" or ”Bar" which had their own RBAC permissions and their actions in the database were audited with the user name.
This is a neat feature, I have used it quite a bit when in multi-tenant applications when high security and tenant data isolation was required. More about this feature here:
Creating a new connection between a client and the database is a heavy operation as it involves networking stuff, several roundtrips, driver client-server (+SSL?) handshake, server-side thread management, etc.
Traditional databases such as MySQL, PostgreSQL and Oracle - all require authentication as part of the creation of the connection.
To avoid the expensive price of frequent creating and closing database connections Backend applications, create and maintain a pool of reusable connections to be handed to arbitrary worker threads to access the database
The only alternative to create those generic pooled connections was to authenticate them with some generic credentials (let's call is "appserver" user) that would have full privileges to all data
This would immediately expose the entire data in the database, and eliminate any security such as RBAC or audit in the data and database level
In it's version 9, Oracle introduced a mechanism called "proxy authentication”, allowing generic authentication for all pooled connections, but re-authentication on that same connection in context
I got lucky. Not really, MongoDB helped a lot, being designed from the ground up for this.
I ran a benchmark that created a MongoTemplate with a borrowed connection from the pool
For a comparison, I added a standard read call of a document from the database
(Both require a roundtrip to the database, authentication is hypothesized to be lighter as it does not involve parsing, data access)
The benchmark tested serial random context switches between 5 tenants
I also tested the times of creating and closing a client connection to MongoDB
To make sure the authentication context switching does not really reconnect the DB
As a comparison between connection creation and authentication
I stopped after 1000 repetitions…
Pooled long lived connections are blank
Authenticated just upon use,
There is no way a connection from the appserver can access all data set. Always a single tenant. Other data is just not available, even in case of a bug or an exploit of a vulnerability in the system…
But what about performance‽
Every worker thread must ask a database connection from a common infrastructure
This common infrastructure would:
Examine the security context of this thread and the injected principal
Borrow a connection from the pool, authenticate it with the current tenant
Hand it over to the requesting worker thread
When done, the worker thread discards this authenticated connection
A blank connection is returned to the pool
Sure it’s easy! When I have different users connecting to the DB. When I have the database being aware to whoever is now connected, authorization (and also audit BTW) are a breeze!
MongoDB does not enable authorization by default. You can enable authorization using the --auth or the --keyFile options, or if using a configuration file, with the security.authorization or the security.keyFile settings
These auditing guarantees require that MongoDB run with journaling enabled.