MongoDB: как безопасно хранить учетные данные?

#javascript #mongodb #authentication #credentials

#javascript #mongodb #аутентификация #учетные данные

Вопрос:

Контекст

В моем текущем проекте веб-приложения я настраиваю базу данных MongoDB, включая администратора сервера и пользователей проекта, с помощью нескольких файлов 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(...) документах явно указано, что должен быть передан пароль открытого текста (за исключением случаев использования внешней пользовательской базы данных).

Серьезно ?!?

Проблема 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

Взято из этого сообщения в блоге:

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

Это действительно работает, но, похоже, не обеспечивает возможности работы с несколькими пользователями. Может быть подход к работе с одним .js файлом для каждого пользователя и / или .js шаблонами файлов, но как только возникает какая-либо сложность, эти файлы должны находиться под контролем версий — и это возвращает нас к той же проблеме, что и при создании пользователей.

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

Зашифрованные / хэшированные / переваренные / любые пароли можно получить, запустив mongo оболочку как 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 true false ошибкой или. Не заглядывая в источники, я могу только предположить, что существует какая sha1(password salt) -то связь с credentials вышесказанным, но, похоже, нет никакой документации, и предполагаемые ошибки, которые мои попытки до сих пор выявили, точно не поощряют дальнейшее изучение этого.

индивидуальный подход

Может быть способ запустить JavaScript, ~/.mongorc.js который принимает текущее имя пользователя (откуда?) и ищет пароль из внешнего источника. Но зачем мне внедрять управление учетными данными для предполагаемого решения для базы данных?

Вопросы:

  1. Как люди обрабатывают учетные данные при работе с MongoDB?
  2. Мой опыт работы с MongoDB до сих пор был настолько ужасным, что, учитывая, что MongoDB продается для производственных целей, кажется разумным сначала поискать причину на моей стороне. Я делаю что-то принципиально неправильное? Мои ожидания неоправданны? Пользователи MongoDB не заботятся о безопасности паролей?

Я был бы очень признателен, если бы кто-нибудь мог поделиться своими подходами и опытом.

Комментарии:

1. Привет @ ssc, тебе удалось найти способ? Это именно то, что я смотрю на данный момент и ничего не могу найти.

2. @Lukino: Нет, извините. Из-за этого — и многих, многих других проблем — я перестал пытаться использовать MongoDB: у меня все еще есть ощущение, что я что-то недопонимаю в корне, но AFAICT, шумиха вокруг MongoDB не оправдана; по крайней мере, это было не так, когда я задавал вопрос 2 года назад.

3. Что ж, мне удалось немного взломать его, поэтому настройка Mongo не является полностью небезопасной — в основном пароли отображаются в открытом виде… По сути, вы создаете пользователя, db.createUser(...) используя поддельный пароль, а затем каким-то образом обновляете его учетные данные до уже хэшированных db.system.users.updateOne({user: "user", {$set: {credentials: "..."}}}) . Не идеальный способ сделать это, но работает для меня. Это позволит мне меньше беспокоиться о наличии незашифрованного пароля на сервере gitlab / chef (хотя для последнего есть зашифрованный пакет данных, который я использовал до сих пор, но я стараюсь его не использовать)

Ответ №1:

Вы можете подготовить свой скрипт в системе управления версиями и предположить, что локальный файл вызывается password.file с ограниченными правами на чтение, содержащий пароль в виде открытого текста.

Затем вы можете запустить скрипт следующим образом:

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

Это позволяет оболочке Mongo считывать пароль из stdin и имеет преимущество в том, что пароль не попадает в систему управления версиями и ps не выводится.

Вероятно, лучшим решением является использование сертификатов x509 для скрипта или переход на 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.

как вы это делаете?

вы можете использовать node.js посылка звонила bcrypt .

Для установки 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 /

Комментарии:

1. Я не совсем понимаю, что вы там делаете, но у меня не может не сложиться впечатление, что вы в корне неправильно поняли мой вопрос: для начала, я не использую node.js . Кроме того, я говорю не о пользователях приложений, а о пользователях базы данных. Кроме того, управление пользователями такой службы, как база данных, с помощью ORM мне не подходит. И последнее, что не менее важно, ваш подход не решает актуальную проблему, поскольку вы все еще храните учетные данные в коде. В любом случае спасибо.

2. Здесь не рассматривается, как установить соединение с базой данных и защитить учетные данные «в состоянии покоя» и в командной строке.