Skip to content

Add actions to verify signup and set password simultaneously#142

Merged
claustres merged 4 commits into
feathersjs-ecosystem:masterfrom
stirling-brandworks:verify-signup-set-password
May 27, 2020
Merged

Add actions to verify signup and set password simultaneously#142
claustres merged 4 commits into
feathersjs-ecosystem:masterfrom
stirling-brandworks:verify-signup-set-password

Conversation

@mattyg

@mattyg mattyg commented May 18, 2020

Copy link
Copy Markdown
Contributor

Summary

This PR implements new actions 'verifySignupSetPasswordLong' and 'verifySignupSetPasswordShort', which allow you to both verify an account and set the user password simultaneously.

This supports the account setup flow in which an administrator creates a user account without a password, and then the user sets their password and verifies their account with a single service call.

Tests and documentation are included.

…tests & documentation.

Add commands 'verifySignupSetPasswordLong' and 'verifySignupSetPasswordShort', which mirror
'verifySignupLong' and 'verifySignupShort' but including hashing and setting the user
password. Upon successful completion, call notifier 'verifySignupSetPassword'.
Add tests for new commands and documentation of new commands.
@claustres

Copy link
Copy Markdown
Collaborator

Hi, thanks for this, it seems to be a good addition. Will have a quick look in the next few days then merge.

@claustres

Copy link
Copy Markdown
Collaborator

I've just tried to merge but there was a conflict on a file so I needed to checkout locally for resolving. However, it now appears that the tests do not work, it is probably linked to the fact the module has been updated to work with Feathers v4 in the meantime. You can check my changes on this branch https://github.com/feathers-plus/feathers-authentication-management/tree/verify-signup-set-password. Could you have a look ? Thanks

@mattyg

mattyg commented May 26, 2020

Copy link
Copy Markdown
Contributor Author

I've just tried to merge but there was a conflict on a file so I needed to checkout locally for resolving. However, it now appears that the tests do not work, it is probably linked to the fact the module has been updated to work with Feathers v4 in the meantime. You can check my changes on this branch https://github.com/feathers-plus/feathers-authentication-management/tree/verify-signup-set-password. Could you have a look ? Thanks

@claustres I merged the upstream changes and updated for compatibility. Let me know if you have any questions or issues.

@claustres claustres merged commit 09b6661 into feathersjs-ecosystem:master May 27, 2020
@claustres

Copy link
Copy Markdown
Collaborator

Good work, thanks.

@mattyg mattyg deleted the verify-signup-set-password branch May 28, 2020 14:56
@GautierT

Copy link
Copy Markdown
Contributor

Hi @mattyg

I think I have found a big security issue.

Because of this line it is possible to set a password without having the right token.

This line await eraseVerifyPropsSetPassword(user1, user1.isVerified, {}, password, field); set the password and patch the user.
Then next line throw an error but the password is already set...
Then the user is still not verified but the password is set.
In my case i don’t check for isVerified when login so then the user can log with this newly created password and take over new unverified account.
So a call to POST /authManagement
with this body

{
    "action": "verifySignupSetPasswordShort",
    "value": {
        "user": {
            "username": "+1234567890"
        },
        "token": "123456",
        "password": "password12345"
    }
}

Will set the password even if token !== verifyShortToken.
Did i miss something or is it a bug ?
Thanks.

@OnnoGabriel

Copy link
Copy Markdown
Contributor

Hi @GautierT, thanks a lot for this important comment and the PR! I tested it locally in my app and have to verify this security issue...

  1. Create a new user
  2. Send a request as described above with a random token.
  3. Password is changed to the one from the request and can be used for a login 😳 (At least if the app does to check for isVerified === true)

This is possible only once. If you try it again, the package returns Verification token has expired., because the token has been deleted.

Best solution for this issue seems to be your PR #167.

PS: Also, I do not see why the verification token should be deleted if someone tries a request with a wrong token. It should stay until a user is using the correct token or until it expires. Or do I miss something?

@claustres

Copy link
Copy Markdown
Collaborator

It's probably not a good idea to let an attacker have chances to guess the token by performing massive tries and errors before the token expire, notably for short tokens, which are subject to brute force attack.

@OnnoGabriel

Copy link
Copy Markdown
Contributor

Sounds reasonable, @claustres. That probably why the token is removed in the standard verify-signup.js, too (see this line).

@GautierT, it is probably a good idea to update your PR and use a function like eraseVerifyProps() (see verify-signup.js), where all tokens are removed but no password is stored. Do you agree?

@GautierT

Copy link
Copy Markdown
Contributor

@claustres : I understand but what prevent the attacker to call resendVerifySignup after each failed attempt and get a new token and try to brute-force it again ?

@OnnoGabriel I will try to improve it now.

@claustres

claustres commented Jun 28, 2021

Copy link
Copy Markdown
Collaborator

It seems to me that if the token is changing each time you call resendVerifySignup the probability to get it right is always 1 over your search space (N), but if you are able to make multiple tries (M) on the same token the probability is higher because each time you try you actually reduce your search space as you already know what tokens will not work and won't need to be tested again. I am not a mathematician but it seems to me that after M tries you get M / N probability vs M / (N - M) probability, so as M gets larger the second one becomes higher faster than the first one.

Anyway, as discussed here (#69), a more secure option for sure is to add a rate-limit layer on top of this package.

@GautierT

GautierT commented Jun 28, 2021

Copy link
Copy Markdown
Contributor

ah yes of course.

multiple tries on the same token the probability is higher because each time you try you actually reduce your search space

Did not though about that !

@GautierT

Copy link
Copy Markdown
Contributor

I added eraseVerifyProps to my PR to reduce risk of brute-force.
It's a copy paste from verify-signup.js

async function eraseVerifyProps (user, isVerified, verifyChanges) {
const patchToUser = Object.assign({}, verifyChanges || {}, {
isVerified,
verifyToken: null,
verifyShortToken: null,
verifyExpires: null,
verifyChanges: {}
});
const result = await usersService.patch(user[usersServiceIdName], patchToUser, {});
return result;
}
}

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants