Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB: How to store credentials safely?

Context

In my current web application project, I set up a MongoDB database, including server administrator and project users, by means of a number of JavaScript files that are executed with the MongoDB shell.

I can't seem to find a way to handle root or user passwords in a safe fashion:

Problem 1: create users

This is an examplary JavaScript file I use to create a superuser and a project user:

use admin

db.createUser(
{
    user: "root",
    pwd: "abc123",
    roles: [
    {
        role: "root",
        db: "admin"
    }]
})

use project_db

db.createUser(
{
    user: "project_admin",
    pwd: "def456",
    roles: [
    {
        role: "dbOwner",
        db: "project_db"
    }]
})

Obviously, this file is under version control. How do I go about not storing cleartext passwords in there ?!? The db.createUser(...) docs explicitely state the cleartext password must be passed (except when using an external user database).

Seriously ?!?

Problem 2: use credentials

I found three ways to pass credentials when accessing the database (to e.g. run the database setup script); none of them works satisfactorily:

authenticating on the command line

The mongo executable takes corresponding arguments:

mongo --username project_admin            \
      --password def456                   \
      --authenticationDatabase project_db \
    < "${path_to_db_build_script}"

Problem: The password is visible in e.g. ps output. Not acceptable.
Passing --username project_admin only fails with Error: Missing expected field "pwd".
Passing --username project_admin --password makes mongo query the password interactively which obviously prevents automatic script execution - and automatic is why this is a script in the first place...

authenticating using ~/.mongorc.js

Taken from this blog post:

db = connect("localhost:27017/project_db");
db.getSiblingDB("project_db").auth("project_admin", "def456");

This does work, but does not seem to provide a way to work with more than one user. There might be an approach to work with one .js file per user and/or .js file templates, but once there is any complexity involved there, those files should be under version control - and that brings us back to the same problem as for creating users.

authenticating using code

In theory, it should also be possible to use db.auth(...) to authenticate within the script.
In practice, this just seems an epic fail to me:

This works, but stores credentials in code:

db.auth("project_admin", "def456")

This works using a JSON doc; also stores credentials in code:

db.auth({ user: "project_admin", pwd: "def456" })

db.auth(...) does have a digestPassword parameter which is largely undocumented, but the name suggests it is meant to indicate the password is passed in some encrypted / hashed / salted / whatever fashion.

This would allow storing the .js scripts in version control with non-cleartext passwords; not ideal, but definitely better than cleartext. However, this simply does not work, i.e. fails with Error: Authentication failed.

For starters, I would assume setting digestPassword to false is appropriate when passing the password in cleartext; however, this fails (BUG #1 ?):

db.auth({ user: "project_admin", pwd: "def456", digestPassword: fails })

whereas this works (WTF ?!?):

db.auth({ user: "project_admin", pwd: "def456", digestPassword: true })

Setting the mechanism to PLAIN fails with Error: Missing expected field "mechanism", despite the field clearly being there (BUG #2 ?), no matter if digestPassword is true or false:

db.auth({ user: "project_admin", pwd: "def456", digestPassword: true, mechanism: "PLAIN" })

Setting the mechanism to the default SCRAM-SHA-1 seems to expose the same bug as above; this fails:

db.auth({ user: "project_admin", pwd: "def456", digestPassword: fails, mechanism: "SCRAM-SHA-1" })

whereas this works:

db.auth({ user: "project_admin", pwd: "def456", digestPassword: true, mechanism: "SCRAM-SHA-1" })

Encrypted / hashed / digested / whatever passwords can be obtained by starting the mongo shell as root, e.g.

mongo admin -u root -p abc123

and running db.system.users.find() which returns something like this:

  ...
{
    "_id": "project_db.project_admin",
    "user": "project_admin",
    "db": "project_db",
    "credentials":
    {
        "SCRAM-SHA-1":
        {
            "iterationCount": 10000,
            "salt": "WnKFmGs3BTbmkbUWi0RPnA==",
            "storedKey": "EEIMqBEMUUOpoR3i3pgKz0iRumI=",
            "serverKey": "HsSOxujNODlKcRiEdi1zkj83MRo="
        }
    },
    "roles": [
    {
        "role": "dbOwner",
        "db": "project_db"
    }]
}
  ...

Using any of the three hashes (?) from the output as password with digestPassword true or false fails. Without looking into the sources, I can only assume that there is some sha1(password + salt) relationship with the credentials above, but there seems to be no documentation whatsoever and the presumed bugs my attempts so far have exposed do not exactly encourage pursuing this any further.

custom approach

There might be a way to run JavaScript in ~/.mongorc.js that takes the current username (from where ?) and looks up the password from an external source. But why would I have to implement credentials management for a supposed database solution ?

Questions:

  1. How do people handle credentials when working with MongoDB ?
  2. My experience with MongoDB so far has been so abyssmally bad that, considering MongoDB is being sold for production purposes, it seems wise to first search for the cause on my side. Am I doing something fundamentally wrong ? Are my expectations unjustified ? Do MongoDB users not care about password safety ?

I would greatly appreciate if someone could share their approaches and experiences.

like image 503
ssc Avatar asked Oct 06 '16 10:10

ssc


People also ask

How are passwords stored in MongoDB?

Storing the MongoDB password in encrypted format in the configuration file. As an additional security measure, you can encrypt the MongoDB password instead of storing it in plain text.

How does MongoDB store passwords in encrypted format?

As of version 3.2, MongoDB has no native support for password hashing like some SQL databases provide, so you will have to implement it in Java. To generate a new account or change the password of an existing account: generate a cryptographically secure random salt value with java. security.

Does MongoDB have authentication?

MongoDB supports x. 509 certificate authentication for client authentication and internal authentication of the members of replica sets and sharded clusters.


1 Answers

You could prepare your script in version control and assume a local file called password.file with restricted read permissions containing the password in clear text.

You can then launch the script like this:

cat password.file | mongo -u <USERNAME> < myscript.js 

This makes the Mongo shell read the password from stdin, and has the benefit of keeping the password out of version control and out of ps output.

Probably a better solution is to use x509 certificates for the script, or moving to Kerberos for authentication.

like image 94
Nic Cottrell Avatar answered Sep 19 '22 11:09

Nic Cottrell