【问题标题】:MongoDB: How to store credentials safely?MongoDB:如何安全地存储凭据?
【发布时间】:2016-10-06 10:39:23
【问题描述】:

上下文

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

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

问题一:创建用户

这是我用来创建超级用户和项目用户的示例 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 文档;还将凭据存储在代码中:

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 用户不关心密码安全吗?

如果有人能分享他们的方法和经验,我将不胜感激。

【问题讨论】:

  • 嗨@ssc,你找到方法了吗?这正是我目前正在寻找的东西,找不到任何东西。
  • @Lukino:不,抱歉。由于这个 - 以及许多其他问题 - 我停止尝试使用 MongoDB:我仍然觉得我从根本上误解了一些东西,但是 AFAICT,围绕 MongoDB 的炒作是不合理的;至少两年前我问这个问题的时候不是这样。
  • 好吧,我设法破解了一点,所以 Mongo 设置并不是完全不安全的 - 主要是有明文密码......基本上你创建一个用户 db.createUser(...) 使用假密码然后你以db.system.users.updateOne({user: "user", {$set: {credentials: "..."}}}) 的方式将他的凭据更新为已经散列的凭据。这不是完美的方法,但对我有用。这会让我不那么担心在 gitlab/chef 服务器上有未加密的密码(虽然对于后者有一个加密的数据包,我一直使用它,但我尽量不使用它)

标签: javascript mongodb authentication credentials


【解决方案1】:

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

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

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

这使得 Mongo shell 从标准输入读取密码,并且有利于使密码不受版本控制和ps 输出的影响。

可能更好的解决方案是使用x509 certificates 编写脚本,或者迁移到 Kerberos 进行身份验证。

【讨论】:

    【解决方案2】:

    如何使用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
    

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

    【讨论】:

      【解决方案3】:

      我假设你正在使用 node.js

      因此,基本方法是进行应用程序端加密,即在将密码等字段保存到 mongoDB 之前,在您的应用程序代码中对其进行加密。

      你是怎么做到的?

      您可以使用名为 bcrypt 的 node.js 包。

      要安装 bcrypt,请使用以下命令:npm install bcrypt –save-dev.

      然后您可以在保存操作上编写一个函数,该函数将在将您想要的字段保存到 MongoDatabase 之前对其进行加密。

      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);
      });
      

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

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

      【讨论】:

      • 我不太明白你在那里做什么,但我不禁觉得你从根本上误解了我的问题:对于初学者来说,我没有使用 node.js。此外,我说的不是应用程序用户,而是数据库用户。此外,通过 ORM 管理数据库等服务的用户也不适合我。最后同样重要的是,您的方法并不能解决实际问题,因为您仍将凭据存储在代码中。还是谢谢。
      • 这并没有说明如何建立与数据库的连接以及如何在“静态”和命令行中保护凭据。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-06
      • 1970-01-01
      • 2012-10-20
      • 2012-10-05
      • 1970-01-01
      • 2018-04-21
      相关资源
      最近更新 更多