web-dev-qa-db-ja.com

Mongoose / MongoDBのパスワードフィールドを保護して、コレクションを作成したときにクエリで返されないようにするにはどうすればよいですか?

2つのコレクション/スキーマがあるとします。 1つは、ユーザー名とパスワードのフィールドを持つユーザースキーマです。次に、作成者フィールドのユーザースキーマへの参照を持つブログスキーマがあります。 Mongooseを使用して

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

ブログドキュメントとユーザーも入力しますが、Mongoose/MongoDBがパスワードフィールドを返さないようにするにはどうすればよいですか?パスワードフィールドはハッシュされますが、返されません。

パスワードフィールドを省略して、簡単なクエリで残りのフィールドを返すことができることは知っていますが、それをどのように設定しますか。また、これを行うエレガントな方法はありますか?

また、状況によっては、ユーザーがログインしたりパスワードを変更したい場合など、パスワードフィールドを取得する必要があります。

74
Luis Elizondo
_.populate('user' , '-password')
_

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

スキーマオプションを使用したJohnnyHKsの回答は、おそらくここに行く方法です。

また、query.exclude()は2.xブランチにのみ存在することに注意してください。

57
aaronheckmann

フィールドの select 属性を使用して、スキーマ定義レベルでデフォルトの動作を変更できます。

password: { type: String, select: false }

その後、必要に応じてfindおよびpopulate呼び出しでフィールド選択を介して'+password'。例えば:

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

編集:

両方のアプローチを試した後、パスポートローカル戦略を使用している何らかの理由で、常に除外するアプローチが機能していないことがわかりました。実際には理由がわかりません。

だから、これは私が最終的に使用したものです:

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

常に除外するアプローチには何の問題もありません。何らかの理由でパスポートで動作しませんでした。私のテストでは、実際にパスワードは希望どおりに除外/含まれていたことがわかりました。 include alwaysアプローチの唯一の問題は、基本的に、データベースに対して行うすべての呼び出しを実行し、多くの作業であるパスワードを除外する必要があることです。


いくつかの素晴らしい答えを見つけた後、これを行うには2つの方法があることがわかりました。「常に時々含めて除外する」と「常に時々除外して含める」ですか?

両方の例:

常に含めるが、時々除外する例:

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

または

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

常に除外するが、時々含む例:

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

ただし、スキーマで定義する必要があります。

password: { type: String, select: false }
17
Luis Elizondo

User.find().select('-password')は正しい答えです。ログインしたい場合、機能しないため、スキーマにselect: falseを追加できません。

10
Ylli Gashi

たとえば、スキーマを使用してこれを実現できます。

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
6
Ikbel

パスワードフィールドが「パスワード」であると仮定すると、次のことができます。

.exclude('password')

より広範な例があります here

それはコメントに焦点を当てていますが、それは同じ原理です。

これは、MongoDBのクエリでプロジェクションを使用し、プロジェクションフィールドに{"password" : 0}を渡すのと同じです。 こちら をご覧ください

3
Adam Comerford

解決策は、プレーンテキストのパスワードを保存しないことです。 bcrypt または password-hash のようなパッケージを使用する必要があります。

パスワードをハッシュする使用例:

 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
2
Cameron Hudson

DocumentToObjectOptionsオブジェクトをschema.toJSON()に渡すことができますまたはschema.toObject()

@ types/mongoose のTypeScript定義を参照してください

 /**
 * 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
netishix

password: { type: String, select: false }を使用している間、認証に必要なときにパスワードも除外することに注意してください。だからあなたが望むようにそれを処理する準備をしてください。

1
Anand Kapdi

Blogs.findOne({ _id: id }, { "password": 0 }).populate("user").exec()

1
Ricky

REST JSON応答のパスワードフィールドを非表示にするために使用しています

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

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

これは元の質問の帰結ですが、これは私の問題を解決しようとして出会った質問でした...

つまり、パスワードフィールドなしでuser.save()コールバックでユーザーをクライアントに戻す方法。

ユースケース:アプリケーションユーザーは、クライアントからプロファイル情報/設定を更新します(パスワード、連絡先情報、whatevs)。 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);
    });
});
0
Bennett Adams