MongoDB:如何安全地存储凭据?

时间:2016-10-06 10:39:24

标签: javascript mongodb authentication credentials

上下文

在我当前的Web应用程序项目中,我通过使用MongoDB shell执行的许多JavaScript文件来设置MongoDB数据库,包括服务器管理员和项目用户。

我似乎无法以安全的方式找到处理root或用户密码的方法:

问题1:创建用户

这是我用来创建超级用户和项目用户的示例JavaScript文件:

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"
    }]
})

显然,此文件受版本控制。如何在那里存储明文密码?!? db.createUser(...) docs明确说明必须传递明文密码(使用外部用户数据库时除外)。

说真的?!?

问题2:使用凭证

我在访问数据库时找到了三种传递凭据的方法(例如运行数据库设置脚本);它们都没有令人满意地工作:

在命令行上进行身份验证

mongo可执行文件采用相应的参数:

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

问题:密码在例如: ps输出。不可接受。
传递--username project_admin仅会因Error: Missing expected field "pwd"而失败 传递--username project_admin --password使mongo以交互方式查询密码,这显然会阻止自动执行脚本 - 而自动就是为什么这首先是脚本...

使用~/.mongorc.js

进行身份验证

取自this blog post

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

这确实有效,但似乎没有提供与多个用户合作的方法。可能有一种方法可以使用一个.js文件每个用户和/或.js文件模板,但一旦涉及任何复杂性,这些文件应该是版本控制 - 这使我们回到了与创建用户相同的问题。

使用代码进行身份验证

理论上,还应该可以使用db.auth(...)在脚本中进行身份验证 在实践中,这对我来说似乎是一个史诗般的失败:

这样可行,但在代码中存储凭据:

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

这可以使用JSON doc;还将凭证存储在代码中:

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

db.auth(...)确实有一个digestPassword参数,这个参数在很大程度上没有记录,但顾名思义它表示密码是以某些加密/散列/加盐/以任何方式传递的。

这将允许在版本控制中使用非明文密码存储.js脚本;不理想,但肯定比明文更好。但是,这只是不起作用,即失败并带有Error: Authentication failed.

对于初学者,我认为在以明文形式传递密码时,将digestPassword设置为false是合适的;但是,这失败了(BUG#1?):

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

虽然这有效(WTF?!?):

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

mechanism设置为PLAIN会失败并显示Error: Missing expected field "mechanism",尽管该字段明显存在(BUG#2?),无论digestPasswordtrue }或false

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

mechanism设置为默认SCRAM-SHA-1似乎会暴露出与上述相同的错误;这失败了:

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

虽然这有效:

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

加密/散列/消化/可以通过mongo shell作为root启动来获取任何密码,例如

mongo admin -u root -p abc123

并运行db.system.users.find(),返回如下内容:

  ...
{
    "_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"
    }]
}
  ...

使用输出中的三个哈希值(?)中的任何一个作为digestPassword truefalse的密码失败。在没有查看来源的情况下,我只能假设与上面的sha1(password + salt)存在一些credentials关系,但似乎没有任何文档,而且到目前为止我尝试的假定错误并不完全是鼓励进一步追求这一点。

自定义方法

可能有一种方法可以在~/.mongorc.js中运行JavaScript,它使用当前用户名(从哪里?)并从外部源查找密码。但为什么我必须为假定的数据库解决方案实施凭证管理?

问题:

  1. 人们在使用MongoDB时如何处理凭据?
  2. 到目前为止,我对MongoDB的体验非常糟糕,以至于考虑到MongoDB是出于生产目的而出售的,首先在我身边寻找原因似乎是明智之举。我做了一些根本错误的事吗?我的期望是不合理的吗? MongoDB用户不关心密码安全吗?
  3. 如果有人可以分享他们的方法和经验,我将不胜感激。

3 个答案:

答案 0 :(得分:0)

如何使用load使用connect命令加载文件:

touch conn.js
chmod 600 conn.js
vim conn.js
# db = connect('localhost/mydb', 'myuser', 'pword');
vim myscript.js
# load('conn.js')
# db.coll.count()
mongo --nodb < myscript.js

这样,您可以安全地拥有多个连接文件,并从版本控制中排除。

答案 1 :(得分:0)

您可以在版本控制中准备脚本,并假设一个名为password.file的本地文件具有受限制的读取权限,其中包含明文密码。

然后您可以像这样启动脚本:

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

这使Mongo Shell从stdin读取密码,并具有使密码不受版本控制和ps输出限制的好处。

也许更好的解决方案是使用x509 certificates作为脚本,或使用Kerberos进行身份验证。

答案 2 :(得分:-3)

我假设您正在使用node.js

所以基本方法是进行应用程序端加密,即在将密码保存到mongoDB之前,在应用程序代码中加密密码等字段。

你是怎么做到的?

您可以使用名为npm install bcrypt –save-dev.的node.js包。

要安装bcrypt,请使用以下命令:var mongoose = require('mongoose'); var bcrypt = require('bcrypt'); var SALT_WORK_FACTOR = 10; mongoose.connect('mongodb://localhost/project_db'); var db = mongoose.connection; db.on('error', function(err){ console.log('connection error', err); }); db.once('open', function(){ console.log('Connection to DB successful'); }); var Schema = mongoose.Schema; var userSchema= new Schema({ name:String, password:String }); var User = mongoose.model('User', userSchema); userSchema.pre('save', function(next){ var user = this; if (!user.isModified('password')) return next(); bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt){ if(err) return next(err); bcrypt.hash(user.password, salt, function(err, hash){ if(err) return next(err); user.password = hash; next(); }); }); }); var testSample = new User({ name: "admin", password: "password1234" }); testSample.save(function(err, data){ if(err) console.log(err); else console.log ('Sucess:' , data); });

然后你可以在保存操作上编写一个函数,它会在将它们保存到MongoDatabase之前加密你想要的字段。

if exists (select * from Table)
BEGIN
EXECUTE msdb.dbo.sp_start_job 'subscription_job_ID_here'
END

您拥有的另一个选项是在数据库级别配置一些安全性。嗯,这是一个巨大的东西,所以我不能在这里总结它。

希望此链接对您有所帮助:https://docs.mongodb.com/manual/security/