컬렉션을 채울 때 쿼리에서 반환되지 않도록 Mongoose / MongoDB의 암호 필드를 보호하는 방법은 무엇입니까?


85

두 개의 컬렉션 / 스키마가 있다고 가정합니다. 하나는 사용자 이름 및 비밀번호 필드가있는 사용자 스키마이고, 작성자 필드에 사용자 스키마에 대한 참조가있는 블로그 스키마가 있습니다. 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

Schema 옵션을 사용하는 JohnnyHK의 대답은 아마도 여기로가는 방법 일 것입니다.

또한 query.exclude()2.x 브랜치에만 존재합니다.


.populate ( 'user': 1, 'password': 0)
Sudhanshu Gaur

"-"를 추가하는 이유를 이해하지 못했고 궁금해서 문서에서 괜찮은 설명을 찾았습니다. 문자열 구문을 사용할 때 경로 앞에-를 붙이면 해당 경로가 제외 된 것으로 표시됩니다. 경로에-접두사가 없으면 포함됩니다. 마지막으로 경로 앞에 +가 붙으면 경로를 강제로 포함하므로 스키마 수준에서 제외 된 경로에 유용합니다. 다음은 mongoosejs.com/docs/api.html#query_Query-select
Connor

304

select필드 의 속성을 사용하여 스키마 정의 수준에서 기본 동작을 변경할 수 있습니다 .

password: { type: String, select: false }

그런 다음 필요에 따라 그것을 해낼 수 findpopulate같은 필드 선택을 통해 호출합니다 '+password'. 예를 들면 :

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

3
큰. find에 추가 할 사람에 대한 예를 제공 할 수 있습니까? 내가 가지고 있다고 가정 : Users.find ({id : _id}) "+ password +를 어디에 추가해야합니까?
Luis Elizondo

제공 한 링크에서 예제를 찾았습니다. mongoosejs.com/docs/api.html#schematype_SchemaType-select 감사합니다
Luis Elizondo

9
이것을 save () 콜백에 전달 된 객체에 적용하는 방법이 있습니까? 따라서 사용자 프로필을 저장할 때 암호가 콜백 매개 변수에 포함되지 않습니다.
Matt

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);
    });

제외 항상 접근 방식에는 아무런 문제가 없으며 어떤 이유로 여권에서 작동하지 않았으며 테스트 결과 실제로 암호가 내가 원할 때 제외 / 포함되었다고 말했습니다. include always 접근 방식의 유일한 문제는 기본적으로 데이터베이스에 대한 모든 호출을 수행하고 많은 작업이 필요한 암호를 제외해야한다는 것입니다.


몇 가지 훌륭한 답변을받은 후 이렇게하는 두 가지 방법이 있음을 알아 냈습니다. "때때로 항상 포함 및 제외"와 "때때로 항상 제외 및 포함"?

둘 다의 예 :

항상 포함하지만 때로는 제외 예제 :

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

또는

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

예외는 항상 있지만 때로는 예를 포함합니다 .

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

그러나 스키마에서 정의해야합니다.

password: { type: String, select: false }

마지막 옵션을 선택하겠습니다. 정말로 필요한 로그인 / passwordUpdate 기능을 제외하고는 절대로 암호를 선택하지 마십시오.
rdrey

어떤 이유로 해당 옵션이 Passport.js 로컬 전략에서 작동하지 않았으므로 이유를 모릅니다.
Luis Elizondo

좋은 대답, 감사합니다 !!! 이유를 알고 있지만 내가 할 때하지 마십시오 .select("+field")그것은 단지 제공 __id에도 불구하고, .select("-field")잘 제외 내가 원하는 분야
레나토 가마

죄송합니다, 그것은 완벽하게 작동, 그 통지를하지 않은 select: false필수입니다
레나토 가마

1
이것은 내 로컬 전략에서 작동합니다. await User.findOne ({email : username}, {password : 1}, async (err, user) => {...});
TomoMiha 2011 년

10

User.find().select('-password')정답입니다. select: false로그인을 원하면 작동하지 않으므로 스키마에 추가 할 수 없습니다 .


로그인 끝점의 동작을 재정의 할 수 없습니까? 그렇다면 이것이 가장 안전한 옵션 인 것 같습니다.
erfling

@erfling, 그렇지 않습니다.
jrran90

1
그것은 const query = model.findOne({ username }).select("+password");로그인 및 암호 변경 / 재설정 경로에서 사용 하고 사용할 수 있으며 그렇지 않으면 절대로 나오지 않도록 할 수 있습니다. 사람들이 실수를 저지르는 것으로 입증 되었기 때문에 기본적으로 반환하지 않는 것이 훨씬 더 안전합니다
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

비밀번호 필드가 "password"라고 가정하면 다음을 수행 할 수 있습니다.

.exclude('password')

여기에 더 광범위한 예가 있습니다.

그것은 댓글에 초점을 맞추고 있지만, 동일한 원리입니다.

이것은 MongoDB의 쿼리에서 프로젝션을 사용 {"password" : 0}하고 프로젝션 필드를 전달하는 것과 같습니다 . 여기를 참조 하십시오


큰. 감사. 나는이 접근 방식을 좋아합니다.
Luis Elizondo

2

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


2

해결책은 일반 텍스트 암호를 저장하지 않는 것입니다. 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

8
암호가 해시되는지 여부에 관계없이 암호 / 해시는 사용자에게 표시되지 않아야합니다. 공격자는 해시 된 암호 (사용 된 알고리즘)에 대한 몇 가지 중요한 정보를 얻을 수 있습니다.
Marco

2

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

스키마 구성에 몇 가지 설정을 추가하여이를 수행하는 다른 방법을 찾았습니다.

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 });

제외 할 필드 이름이있는 toJSON 객체에 변환 함수를 추가합니다. 같이 언급 문서 :

민감한 정보를 제거하거나 사용자 지정 개체를 반환하는 등 일부 기준에 따라 결과 개체를 변환해야 할 수도 있습니다. 이 경우 옵션 transform기능 을 설정합니다 .


2
router.get('/users',auth,(req,res)=>{
   User.findById(req.user.id)
    //skip password
    .select('-password')
    .then(user => {
        res.json(user)
    })
})

1

사용하는 동안 password: { type: String, select: false }인증에 필요할 때 암호도 제외된다는 점을 명심해야합니다. 그러니 원하는대로 처리 할 준비를하십시오.


0

이것은 원래 질문에 대한 추론에 가깝지만 이것은 내 문제를 해결하려고 시도한 질문이었습니다 ...

즉, 암호 필드없이 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

const userSchema = new mongoose.Schema(
  {
    email: {
      type: String,
      required: true,
    },
    password: {
      type: String,
      required: true,
    },
  },
  {
    toJSON: {
      transform(doc, ret) {
        delete ret.password;
        delete ret.__v;
      },
    },
  }
);

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.