Як захистити поле пароля в 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.