¿Cómo implementar la técnica de hashing en un proyecto Mongo?
El hashing es una técnica esencial para la seguridad de las contraseñas en las bases de datos. La implementación correcta asegura que las contraseñas almacenadas estén protegidas, incluso si la base de datos es comprometida. Aquí se explica paso a paso cómo aplicar el hashing en un proyecto basado en MongoDB usando la librería bcrypt.
¿Cómo instalar la librería bcrypt?
Para comenzar, es necesario instalar la librería bcrypt. Esta librería permite realizar el hashing de contraseñas de manera eficiente y segura. Sigue estos pasos:
Instala bcrypt junto con su tipado (para TypeScript), utilizando el siguiente comando:
Asegúrate de que la instalación del tipado es solo para el entorno de desarrollo.
¿Cómo modificar el servicio de usuarios?
La lógica para manejar contraseñas debe ser incorporada en el servicio de usuarios de tu aplicación. Aquí es donde se recibe la contraseña y se realiza el hashing antes de guardarla en la base de datos.
Importa la librería en el archivo del servicio.
import*as bcryptfrom'bcrypt';
Implementa el hashing en el método de creación de usuarios antes de guardar los datos.
Asegúrate de usar findOne para evitar que se devuelvan múltiples resultados, dado que los emails deben ser únicos en la base de datos.
¿Cómo exportar el servicio para su uso en otros módulos?
El servicio debe ser accesible desde otros módulos, como el de autenticación, para funciones como verificar usuarios:
Exporta el servicio de usuarios desde su módulo:
exportclassUserService{// Métodos aquí}
Asegúrate de que otros módulos pueden importar y utilizar el servicio sin problemas.
Este proceso de implementación garantiza que las contraseñas de los usuarios estén protegidas y el sistema sea seguro. ¡Continúa practicando y mejorando tus habilidades! Recuerda que la seguridad es esencial en cualquier aplicación.
Otra forma para eliminar el password de la respuesta pueden instalar el paquete:
npm i nestjs-mongoose-exclude luego en su Entity utilizar el decorador @ExcludeProperty() y por último utilizar un Interceptor a nivel de ruta o del controlador completo
quedaría así:
Eso les devolvera todo el objeto del usuario menos el password
Hola 👋
Yo implementaria algo mas reutilizable y que no toca al users.services.
Estando en el user.entity agregaria una funcion al metodo con el metodo pre de UserSchema y sobreescribiria el metodo toJSON del UserScham.
// user.entity.ts...UserSchema.pre('save',asyncfunction(next:HookNextFunction){const user =thisasUser;// only hash the password if it has been modified (or is new)if(!user.isModified('password'))returnnext();// Random additional dataconst salt =await bcrypt.genSalt(10);const hash =await bcrypt.hash(user.password, salt);// Replace the password with the hash user.password= hash;returnnext();});
Hasheara el password en la creacion y en la modificacion de la misma.
Sobreescribira un poco el metodo toJSON para que ignore el password y en este caso el __v al imprimir el usuario en cualquier tipo de retorno del mismo, esto lo hace super reutilizable y no tendremos que tocar nunca mas la config.
No podría llamarle reutilizable a los pre middlewares de mongoose, dado que se esta acoplando lógica a la entidad (por lo tanto a la base de datos) y hay casos puntuales en los que los pre middlewares no son accionados o llamados, ejemplo: un update many o un updateOne con set directo, esto es algo para tener en cuenta y evaluar con el equipo de desarrollo. (Como una nota: se puede hacer genérica la función para todos los pre middlewares disponibles, pero no creo que lo valga, igual es cosa que se evalué con el equipo de desarrollo).
El caso del toJSON, lo encuentro útil (salvo que en queries de tipo lean, este no funciona).
El caso genérico que se me ocurre es:
Marcar la propiedad del schema como {select: false} de acuerdo a la documentación de mongoose.
Posterior es forzar la selección del password, que para la autenticación es un caso especial (por lo general siempre no es seleccionado en las consultas tipo find({})).
Y finalmente eliminar en el servicio o un repositorio como se ve en la clase para mantener desacoplado lo relacionado a la BD.
Igual espero aportarles algo 👨🏾💻
Otra forma para esconder el password es agregando la siguiente configuración en el user.entity:
Con select: false indicamos que todas las peticiones de los usuarios vendrán sin el password :)
Muy buena solución, ya que como si lo haciamos como el profe solo nos funciona al crear y no en todas las consultas. alguna forma de no retornar el '_v':0 ?
npm i bcrypt
npm i -D @types/bcrypt
La mejor forma de excluir los password lo encontré en la documentación de NestJs:
Una alternativa para devolver el objeto sin la contraseña podría ser usar la Keyword Delete de JS
delete model.password
Con el decorador @Schema se puede modificar el método toJSON que mongoose usa en la entidad / Modelo de mongoose:
Para eliminar password en el método create del servicio:
users.service.ts:
asynccreate(payload:CreateUserDto){const newUser =newthis.userModel(payload);// Hasheamos la contraseña y la guardamos en una variableconst hashedPassword =await bcrypt.hash(newUser.password,10);// Actualizamos la propiedad password de newUser por hashedPassword newUser.password= hashedPassword;// Guardamosconst userSaved =await newUser.save();// Creamos una variable que guarde el usuario en formato JSONconst user = userSaved.toJSON();// Eliminamos la contraseñadelete user.password;// Retornamos el userreturn user;}
Y luego para que en las demás peticiones http (Get, Put y Delete) no aparezca la propiedad password