Як захистити поле пароля в Mongoose / MongoDB, щоб воно не поверталось у запиті, коли я заповнюю колекції?


86

Припустимо, у мене є дві колекції / схеми. Одним із них є схема користувачів із полями імені користувача та пароля, тоді у мене є схема блогів, яка має посилання на схему користувачів у полі автора. Якщо я використовую Mongoose, щоб зробити щось подібне

Blogs.findOne({...}).populate("user").exec()

У мене буде заповнений документ блогу та користувач, але як я можу завадити Mongoose / MongoDB повертати поле пароля? Поле пароля хешоване, але його не слід повертати.

Я знаю, що можу пропустити поле пароля та повернути решту полів у простому запиті, але як це зробити за допомогою заповнення. Крім того, чи є якийсь елегантний спосіб це зробити?

Крім того, в деяких ситуаціях мені потрібно отримати поле для пароля, наприклад, коли користувач хоче увійти в систему або змінити пароль.


ви також можете зробити .populate ('user': 1, 'password': 0)
— Sudhanshu Gaur

Відповіді:


66
.populate('user' , '-password')

http://mongoosejs.com/docs/populate.html

Відповідь JohnnyHKs, використовуючи параметри схеми, - це, мабуть, шлях сюди.

Також зверніть увагу, що query.exclude()існує лише у гілці 2.x.


це також буде працювати .populate ('user': 1, 'password': 0)
— Sudhanshu Gaur

Я не розумів, чому додавання "-" працює, і мені було цікаво, тому я знайшов пристойне пояснення в документах: При використанні синтаксису рядка префікс шляху до - позначить цей шлях як виключений. Якщо шлях не має префікса -, він включається. Нарешті, якщо шлях має префікс +, він змушує включати шлях, що корисно для шляхів, виключених на рівні схеми. Ось посилання на повну річ mongoosejs.com/docs/api.html#query_Query-select
— Коннор

305

Ви можете змінити поведінку за замовчуванням на рівні визначення схеми, використовуючи selectатрибут поля:

password: { type: String, select: false }

Тоді ви можете втягнути його за потребою findта populateвикликати через вибір поля як '+password'. Наприклад:

Users.findOne({_id: id}).select('+password').exec(...);

3
Чудово. Чи можете ви навести приклад того, кого додати у пошук? Якщо припустити, що у мене є: Users.find ({id: _id}), куди мені додати "+ пароль +?"
— Луїс Елізондо

Знайшли приклад за наданим вами посиланням. mongoosejs.com/docs/api.html#schematype_SchemaType-select Дякую
— Луїс Елізондо

9
Чи є спосіб застосувати це до об’єкта, переданого для збереження () зворотного виклику? Такий, що коли я зберігаю профіль користувача, пароль не включається в параметр зворотного виклику.
— Метт

2
На мій погляд, це, безумовно, найкраща відповідь. Додайте це один раз, і воно буде виключено. Набагато краще, ніж додавання параметра вибору або виключення до кожного запиту.
— AndyH

Це має бути остаточна відповідь. Додано до схеми, і не потрібно забувати про виключення під час запитів.
— KhoPhi

23

Редагувати:

Спробувавши обидва підходи, я виявив, що підхід виключення завжди не працює для мене з якихось причин, використовуючи локальну паспортну стратегію, насправді не знаю чому.

Отже, ось що я в підсумку використав:

Blogs.findOne({_id: id})
    .populate("user", "-password -someOtherField -AnotherField")
    .populate("comments.items.user")
    .exec(function(error, result) {
        if(error) handleError(error);
        callback(error, result);
    });

У підході виключити завжди немає нічого поганого, він просто не працював з паспортом з якихось причин, мої тести сказали мені, що насправді пароль виключався / включався, коли я хотів. Єдина проблема підходу завжди включає те, що мені в основному потрібно пройти кожен дзвінок до бази даних і виключити пароль, що є великою роботою.


Після декількох чудових відповідей я виявив, що це можна зробити двома способами: "завжди включати та виключати іноді" та "завжди виключати та іноді включати"?

Приклад обох:

Приклад включати завжди, але іноді виключати :

Users.find().select("-password")

або

Users.find().exclude("password")

Виняток завжди, але іноді включає приклад:

Users.find().select("+password")

але ви повинні визначити в схемі:

password: { type: String, select: false }

Я піду на останній варіант. Ніколи не вибирайте пароль, за винятком функцій входу / оновлення пароля, які вам дійсно потрібні.
— Рдрі

З якоїсь причини ця опція не працювала з локальною стратегією Passport.js, не знаю чому.
— Луїс Елізондо

Гарна відповідь, спасибі !!! Не знаю чому, але коли я .select("+field")це роблю, це приносить лише те __id, хоча .select("-field")добре виключає поле, яке я хочу
— Ренато Гама

Вибачте, це працює ідеально, не помітив, що select: falseце обов’язково
— Ренато Гама

1
Це працює для моєї локальної стратегії: await User.findOne ({email: username}, {password: 1}, async (err, user) => {...});
— TomoMiha

10

User.find().select('-password')- правильна відповідь. Ви не можете додавати select: falseсхему, оскільки вона не буде працювати, якщо ви хочете увійти.


Чи не можете ви замінити поведінку в кінцевій точці входу? Якщо так, це здається найбезпечнішим варіантом.
— ерфлінг

@erfling, не буде.
— jrran90

1
Це буде, ви можете використовувати const query = model.findOne({ username }).select("+password");та використовувати це на маршрутах зміни / скидання логіна та пароля, а в іншому випадку переконатися, що воно ніколи не виходить. Це набагато безпечніше, щоб він не повертався за замовчуванням, оскільки доведено, що люди помиляються imo
— Cacoon

10

Ви можете досягти цього, використовуючи схему, наприклад:

const UserSchema = new Schema({/* */})

UserSchema.set('toJSON', {
    transform: function(doc, ret, opt) {
        delete ret['password']
        return ret
    }
})

const User = mongoose.model('User', UserSchema)
User.findOne() // This should return an object excluding the password field

3
Я перевірив кожну відповідь, думаю, це найкращий варіант, якщо ви розробляєте api.
— asmmahmud

Це не видаляє поля з населенням.
— JulianSoto

1
Найкращий варіант для мене, такий підхід не суперечить моєму способу автентифікації
— Mathiasfc

Це найкращий спосіб, якщо ви використовуєте API. Вам ніколи не доведеться турбуватися про те, що забудете видалити поле :)
— Сем Манро,

4

Я використовую для приховування поля пароля в моїй відповіді REST JSON

UserSchema.methods.toJSON = function() {
 var obj = this.toObject(); //or var obj = this;
 delete obj.password;
 return obj;
}

module.exports = mongoose.model('User', UserSchema);

3

Якщо припустити, що поле вашого пароля - "пароль", ви можете просто зробити:

.exclude('password')

Існує більш великий приклад тут

Це зосереджено на коментарях, але це той самий принцип у грі.

Це те саме, що використовувати проекцію в запиті в MongoDB і передати {"password" : 0}в поле проекції. Дивіться тут


Чудово. Дякую. Мені подобається такий підхід.
— Луїс Елізондо


2

Рішення полягає в тому, щоб ніколи не зберігати відкриті текстові паролі. Ви повинні використовувати такий пакет, як bcrypt або хеш пароля .

Приклад використання хешу пароля:

 var passwordHash = require('password-hash');

    var hashedPassword = passwordHash.generate('password123');

    console.log(hashedPassword); // sha1$3I7HRwy7$cbfdac6008f9cab4083784cbd1874f76618d2a97

Приклад використання для перевірки пароля:

var passwordHash = require('./lib/password-hash');

var hashedPassword = 'sha1$3I7HRwy7$cbfdac6008f9cab4083784cbd1874f76618d2a97';

console.log(passwordHash.verify('password123', hashedPassword)); // true
console.log(passwordHash.verify('Password0', hashedPassword)); // false

8
Незалежно від того, хешується пароль чи ні, пароль / хеш ніколи не повинен відображатися користувачеві. Зловмисник може отримати важливу інформацію про хешований пароль (який алгоритм був використаний) тощо.
— Марко

2

Ви можете передати об'єкт DocumentToObjectOptions до schema.toJSON () або schema.toObject () .

Див. Визначення TypeScript із @ types / mongoose

 /**
 * The return value of this method is used in calls to JSON.stringify(doc).
 * This method accepts the same options as Document#toObject. To apply the
 * options to every document of your schema by default, set your schemas
 * toJSON option to the same argument.
 */
toJSON(options?: DocumentToObjectOptions): any;

/**
 * Converts this document into a plain javascript object, ready for storage in MongoDB.
 * Buffers are converted to instances of mongodb.Binary for proper storage.
 */
toObject(options?: DocumentToObjectOptions): any;

DocumentToObjectOptions має опцію перетворення, яка запускає власну функцію після перетворення документа в об'єкт javascript. Тут ви можете приховати або змінити властивості, щоб задовольнити ваші потреби.

Отже, припустимо, ви використовуєте schema.toObject () і хочете приховати шлях пароля від вашої схеми користувача. Вам слід налаштувати загальну функцію перетворення, яка буде виконуватися після кожного виклику toObject ().

UserSchema.set('toObject', {
  transform: (doc, ret, opt) => {
   delete ret.password;
   return ret;
  }
});

2

Я знайшов інший спосіб зробити це, додавши деякі налаштування до конфігурації схеми.

const userSchema = new Schema({
    name: {type: String, required: false, minlength: 5},
    email: {type: String, required: true, minlength: 5},
    phone: String,
    password: String,
    password_reset: String,
}, { toJSON: { 
              virtuals: true,
              transform: function (doc, ret) {
                delete ret._id;
                delete ret.password;
                delete ret.password_reset;
                return ret;
              }

            }, timestamps: true });

Додавши функцію перетворення до об’єкта JJSON з назвою поля для виключення. як зазначено в документах :

Можливо, нам доведеться виконати перетворення отриманого об'єкта на основі деяких критеріїв, скажімо, щоб видалити якусь конфіденційну інформацію або повернути користувацький об'єкт. У цьому випадку ми встановлюємо додаткову transformфункцію.



1

Під час використання password: { type: String, select: false }слід пам’ятати, що він також виключає пароль, коли він нам потрібен для автентифікації. Тож будьте готові впоратися з цим, як завгодно.



0

Це скоріше наслідки вихідного питання, але це питання, з яким я зіткнувся, намагаючись вирішити свою проблему ...

А саме, як відправити користувача назад до клієнта у зворотному виклику user.save () без поля пароля.

Приклад використання: користувач програми оновлює інформацію про свій профіль / налаштування від клієнта (пароль, контактна інформація, дані). Ви хочете надіслати оновлену інформацію про користувача назад клієнту у відповіді, як тільки вона буде успішно збережена в mongoDB.

User.findById(userId, function (err, user) {
    // err handling

    user.propToUpdate = updateValue;

    user.save(function(err) {
         // err handling

         /**
          * convert the user document to a JavaScript object with the 
          * mongoose Document's toObject() method,
          * then create a new object without the password property...
          * easiest way is lodash's _.omit function if you're using lodash 
          */

         var sanitizedUser = _.omit(user.toObject(), 'password');
         return res.status(201).send(sanitizedUser);
    });
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.