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:
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 ?!?
I found three ways to pass credentials when accessing the database (to e.g. run the database setup script); none of them works satisfactorily:
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...
~/.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.
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.
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 ?
I would greatly appreciate if someone could share their approaches and experiences.
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.
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.
MongoDB supports x. 509 certificate authentication for client authentication and internal authentication of the members of replica sets and sharded clusters.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With