scotch-io/easy-node-authentication

Critical Security Flaw - Mongoose Model inside Webtoken

Mydayyy opened this issue · 3 comments

Hi,

I noticed that you do have your full mongoose model inside the generated json webtoken. This is a huge security flaw, as a json webtoken is only signed but not encrypted, meaning that everyone who can retrieve a token from a user ( e.g sniffing ) is able to obtain the users cleartext password, even from expired tokens.

https://github.com/scotch-io/node-token-authentication/blob/master/server.js#L81

Please consider fixing this asap

~Mydayyy

Edit: I got asked to go a little more indepth about the underlying security issue here.

So basically, take a look at the line https://github.com/scotch-io/node-token-authentication/blob/master/server.js#L81

You see that the token gets created with jwt.sign(user, app.get('sup erSecret'), { expiresIn: 86400 // expires in 24 hours }). First argument, the user, is passed by mongodb as a response to the findOne query. This object contains all model data. Now, that user object is put into the JWT as a payload.

A JsonWebToken is signed but not encrypted, that means while we can verify that the content has not changed, it is still public for everyone. If you now receive a token by the API it contains all user information, including the cleartext password.

See the following webtoken for example ( which is returned by this API ):

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyIkX18iOnsic3RyaWN0TW9kZSI6dHJ1ZSwic2VsZWN0ZWQiOnt9LCJnZXR0ZXJzIjp7fSwiX2lkIjoiNTljZTRhNTUyZDQwODQ2YzBiOTM4MDg1Iiwid2FzUG9wdWxhdGVkIjpmYWxzZSwiYWN0aXZlUGF0aHMiOnsicGF0aHMiOnsiX192IjoiaW5pdCIsImFkbWluIjoiaW5pdCIsInBhc3N3b3JkIjoiaW5pdCIsIm5hbWUiOiJpbml0IiwiX2lkIjoiaW5pdCJ9LCJzdGF0ZXMiOnsiaWdub3JlIjp7fSwiZGVmYXVsdCI6e30sImluaXQiOnsiX192Ijp0cnVlLCJhZG1pbiI6dHJ1ZSwicGFzc3dvcmQiOnRydWUsIm5hbWUiOnRydWUsIl9pZCI6dHJ1ZX0sIm1vZGlmeSI6e30sInJlcXVpcmUiOnt9fSwic3RhdGVOYW1lcyI6WyJyZXF1aXJlIiwibW9kaWZ5IiwiaW5pdCIsImRlZmF1bHQiLCJpZ25vcmUiXX0sInBhdGhzVG9TY29wZXMiOnt9LCJlbWl0dGVyIjp7ImRvbWFpbiI6bnVsbCwiX2V2ZW50cyI6e30sIl9ldmVudHNDb3VudCI6MCwiX21heExpc3RlbmVycyI6MH19LCJpc05ldyI6ZmFsc2UsIl9kb2MiOnsiX192IjowLCJhZG1pbiI6dHJ1ZSwicGFzc3dvcmQiOiJwYXNzd29yZCIsIm5hbWUiOiJOaWNrIENlcm1pbmFyYSIsIl9pZCI6IjU5Y2U0YTU1MmQ0MDg0NmMwYjkzODA4NSJ9LCIkaW5pdCI6dHJ1ZSwiaWF0IjoxNTA2NjkxNzIyLCJleHAiOjE1MDY3NzgxMjJ9.WFZU_FwYzglN0vagVvBn4NYEaOS2tmKbUjrDUyhp8fQ

Go to https://jwt.io/ and select Debugger in the navigation panel. Now paste that token there. It will display the content which is the following:

  "$__": {
    "strictMode": true,
    "selected": {},
    "getters": {},
    "_id": "59ce4a552d40846c0b938085",
    "wasPopulated": false,
    "activePaths": {
      "paths": {
        "__v": "init",
        "admin": "init",
        "password": "init",
        "name": "init",
        "_id": "init"
      },
      "states": {
        "ignore": {},
        "default": {},
        "init": {
          "__v": true,
          "admin": true,
          "password": true,
          "name": true,
          "_id": true
        },
        "modify": {},
        "require": {}
      },
      "stateNames": [
        "require",
        "modify",
        "init",
        "default",
        "ignore"
      ]
    },                 
    "pathsToScopes": {},
    "emitter": {
      "domain": null,
      "_events": {},
      "_eventsCount": 0,
      "_maxListeners": 0
    }
  },
  "isNew": false,
  "_doc": {
    "__v": 0,{
  "$__": {
    "strictMode": true,
    "selected": {},
    "getters": {},
    "_id": "59ce4a552d40846c0b938085",
    "wasPopulated": false,
    "activePaths": {
      "paths": {
        "__v": "init",
        "admin": "init",
        "password": "init",
        "name": "init",
        "_id": "init"
      },
      "states": {
        "ignore": {},
        "default": {},
        "init": {
          "__v": true,
          "admin": true,
          "password": true,
          "name": true,
          "_id": true
        },
        "modify": {},
        "require": {}
      },
      "stateNames": [
        "require",
        "modify",
        "init",
        "default",
        "ignore"
      ]
    },
    "pathsToScopes": {},
    "emitter": {
      "domain": null,
      "_events": {},
      "_eventsCount": 0,
      "_maxListeners": 0
    }
  },
  "isNew": false,
  "_doc": {
    "__v": 0,
    "admin": true,
    "password": "password",
    "name": "Nick Cerminara",
    "_id": "59ce4a552d40846c0b938085"
  },
  "$init": true,
  "iat": 1506691722,
  "exp": 1506778122
}
    "admin": true,
    "password": "password",
    "name": "Nick Cerminara",
    "_id": "59ce4a552d40846c0b938085"
  },
  "$init": true,
  "iat": 1506691722,
  "exp": 1506778122
}

This means that everyone who can receive that token has all information about the user. For example, if we would insert an email field for the user it will also show up there. This is even true for expired tokens. So every token the user ever created has all information about him inside, including the cleartext password.

This is obviously a huge security flaw.
A possible fix would be to only include selected attributes inside the payload of the token, not the entire model.

Further edit: Accidentally closed this issue. Lesson learned. Look where you click! Sorry! #

Hey thanks so much for the detailed report! This is definitely a big issue and one we'd like to fix up.

Would you have time to send over a pull request with the fix and I can get this repo and the article updated and provide the proper credits to you in the article.

Moved issue to another repo: scotch-io/node-token-authentication#8 - confused the tabs when I wrote that issue :|. Preparing a PR right now.

Thx Mydayyy for the more detailed report.