¿Cómo proteger el campo de contraseña en Mongoose / MongoDB para que no vuelva en una consulta cuando llene las colecciones?

Supongamos que tengo dos colecciones / esquemas. Uno es el Esquema de usuarios con campos de nombre de usuario y contraseña, luego, tengo un Esquema de blogs que tiene una referencia al Esquema de usuarios en el campo de autor. Si uso Mongoose para hacer algo como

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

Tendré el documento del blog y el usuario también rellenado, pero ¿cómo evito que Mongoose / MongoDB devuelva el campo de contraseña? El campo de contraseña es hash pero no debe ser devuelto.

Sé que puedo omitir el campo de contraseña y devolver el rest de los campos en una consulta simple, pero ¿cómo hago eso con poblar? Además, ¿hay alguna forma elegante de hacer esto?

Además, en algunas situaciones necesito obtener el campo de contraseña, como cuando el usuario quiere iniciar sesión o cambiar la contraseña.

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

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

La respuesta de JohnnyHKs usando las opciones de esquema es probablemente el camino a seguir aquí.

También tenga en cuenta que query.exclude() solo existe en la twig 2.x.

Puede cambiar el comportamiento predeterminado en el nivel de definición de esquema utilizando el atributo select del campo:

 password: { type: String, select: false } 

Luego, puede instalarlo según sea necesario para find y completar llamadas mediante la selección de campo como '+password' . Por ejemplo:

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

Editar:

Después de probar ambos enfoques, descubrí que el enfoque de excluir siempre no funcionaba para mí por algún motivo, utilizando la estrategia local del pasaporte, realmente no sé por qué.

Entonces, esto es lo que terminé usando:

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

No hay nada malo con el enfoque de excluir siempre, simplemente no funcionó con el pasaporte por alguna razón, mis pruebas me dijeron que, de hecho, la contraseña se excluía / incluía cuando yo quería. El único problema con el enfoque de incluir siempre es que, básicamente, necesito pasar por cada llamada que hago a la base de datos y excluir la contraseña, que es mucho trabajo.


Después de un par de excelentes respuestas, descubrí que hay dos formas de hacerlo: “siempre incluir y excluir algunas veces” y “excluir e incluir siempre algunas veces”.

Un ejemplo de ambos:

El incluir siempre pero excluir a veces ejemplo:

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

o

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

El exlucio siempre pero a veces incluye ejemplos:

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

pero debes definir en el esquema:

 password: { type: String, select: false } 

User.find().select('-password') es la respuesta correcta. No puede agregar select: false en el esquema ya que no funcionará, si desea iniciar sesión.

Suponiendo que su campo de contraseña es “contraseña”, puede hacer lo siguiente:

 .exclude('password') 

Hay un ejemplo más extenso aquí

Eso se centra en los comentarios, pero es el mismo principio en juego.

Esto es lo mismo que usar una proyección en la consulta en MongoDB y pasar {"password" : 0} en el campo de proyección. Mira aquí

Puede lograr eso usando el esquema, por ejemplo:

 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 

La solución es nunca almacenar contraseñas de texto plano. Debe usar un paquete como [bcrypt] 1 o [password-hash] 2 .

Ejemplo de uso para hash la contraseña:

  var passwordHash = require('password-hash'); var hashedPassword = passwordHash.generate('password123'); console.log(hashedPassword); // sha1$3I7HRwy7$cbfdac6008f9cab4083784cbd1874f76618d2a97 

Ejemplo de uso para verificar la contraseña:

 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 

Esto es más un corolario de la pregunta original, pero esta fue la pregunta que encontré tratando de resolver mi problema …

A saber, cómo enviar al usuario de vuelta al cliente en la callback user.save () sin el campo de contraseña.

Caso de uso: el usuario de la aplicación actualiza su información / configuración de perfil del cliente (contraseña, información de contacto, whatevs). Desea enviar nuevamente la información de usuario actualizada al cliente en la respuesta, una vez que se haya guardado correctamente en 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); }); }); 

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

Al utilizar la password: { type: String, select: false } , debe tener en cuenta que también excluirá la contraseña cuando la necesitemos para la autenticación. Así que prepárate para manejarlo como quieras.