Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sendResetPwd, information leak #85

Closed
sylvainlap opened this issue Jan 15, 2018 · 8 comments
Closed

sendResetPwd, information leak #85

sylvainlap opened this issue Jan 15, 2018 · 8 comments

Comments

@sylvainlap
Copy link
Contributor

sylvainlap commented Jan 15, 2018

With the sendResetPwd action, if the email address does not exist, a 404 error is thrown.

I think this is an information leak, because thanks to that call, an attacker can find if an email address is valid or not (ie in the database). I think this call should not return the user info, and send the same empty response, whatever the case (user exists or not).

Regards.

@jonnyparris
Copy link

In case it helps anyone else, I wrote a middleware handler to intercept any 400 errors as a workaround.
This way you can still benefit from the explicit error messages server side but any client side attackers/users receive a generic response:

module.exports = function(app) {
  return function(error, req, res, next) {
    let refererPath = req.headers.referer.split('/')
    refererPath = refererPath[refererPath.length-1]
    const resetPwdRequested = refererPath === 'forgot-password'
    const userNotFoundError = error && error.code === 400 && error.name === 'BadRequest' && error.message === 'User not found.'

    if (resetPwdRequested && userNotFoundError) {
      // Don't leak whether or not users exist in your DB to nefarious hackers
      // (Make sure this response is the same as when a user is found.)
      res.status(201).send({ success: true })
    }
    next(error)
  }
}

@jraut
Copy link

jraut commented Apr 24, 2018

The same applies to error responses which result from unique violation for email. The instance details are revealed which contain the hashed password (well, any fields which were part of the original payload). This enables gathering a map of pwd => encrypt(pwd) and any other processing of the fields.

@musicformellons
Copy link

musicformellons commented May 23, 2018

I try to find the 404 response referred above, and would expect it to show up in my chrome network tab in the status column. However I do not get any 404 (just 200) when using a not existing email. Does this mean my app is 'not vulnerable' to this kind of leaking? And why: could it be because I use websockets?

Server side I get:

error:  BadRequest: User not found.
    at new BadRequest(~/api_server/node_modules/@feathersjs/errors/lib/index.js:86:17)
    at getUserData (~/api_server/node_modules/feathers-authentication-management/lib/helpers.js:93:11)
    at ~/api_server/node_modules/feathers-authentication-management/lib/sendResetPwd.js:32:14

@claustres
Copy link
Collaborator

Well I am afraid sending 200 when there is actually an error is not really great, how do you let your users (and not attackers) know about any error ? To check the email an attacker can also try to register as it is often required to be unique and check this error, if no error is returned how do you tell your user that he has to provide another email ?

Regarding #85 (comment) you probably miss a removeVerification hook to avoid leaking information.

@jonnyparris
Copy link

how do you let your users (and not attackers) know about any error ?

You set a generic message that your users will receive a password reset email if their details are found in your records.

To check the email an attacker can also try to register as it is often required to be unique and check this error...

True, but generally my main aim is to slow down / prevent rapid enumeration of multiple emails - something like a captcha on your registration form can help with this.

@claustres
Copy link
Collaborator

Yes or rate limiting.

@eddyystop
Copy link
Collaborator

@OnnoGabriel
Copy link
Contributor

This is an old and closed issue. But in case anybody searches for a solution for this issue that works with REST and Websocket transport: You can always remove the error and unify the result that is returned to the frontend in the error and after hooks of a Feathers service. Here is a simple example for the sendResetPwd action of our auth-management' service:

  app.service('/auth-management').hooks({
    error: {
      all: [
        // Delete error and return only true after "sendResetPwd" request
        // => to hide any existence or not-existence of users
        (context) => {
          if (context.data && context.data.action === 'sendResetPwd') {
            delete context.error;
            context.result = true;
          }
        },
      ],
    },
    after: {
      all: [
        // Return only true after "sendResetPwd" request
        // => to hide any existence or not-existence of users
        (context) => {
          if (context.data && context.data.action === 'sendResetPwd') {
            context.result = true;
          }
        },
      ],
    },
  });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants