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

feat(security): RN-1303: Update password storage to use argon2 #5872

Open
wants to merge 16 commits into
base: dev
Choose a base branch
from

Conversation

tcaiger
Copy link
Contributor

@tcaiger tcaiger commented Sep 3, 2024

Issue #: feat(security): RN-1303: Update password storage

Changes:

  • Install @node-rs/argon2 in auth package
  • Update encryptPassword and hashAndSaltPassword helper functions to use argon2 hashing
  • Refactor encryptPassword through out the mono repo to be async
  • Add verifyPassword helper to verify argon2 password hashes
  • Refactor authentication code throughout the mono repo to use verifyPassword instead of checking sha256 hashes
  • Add migration to hash all existing passwords with argon2
  • Update User checkPassword method to first check for argon2 hashes and then check for migrated passwords that are hashed with both sha256 and argon2

`);
const users = await db.runSql('SELECT id, password_hash_old, password_salt FROM user_account');

await Promise.all(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are about 4,400 user_account records in the database so this set of updates take 5 - 6 minutes (on my local) . I'm not sure if there is a precedent for long migrations like this or if it is a problem?!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a chat about this with @rohan-bes and we agreed that it's fine. Our rule of thumb is

  • Sub 10 mins is totally fine
  • 10+ worth having a discussion with the team but generally safe
  • 1 hour + probably prefer to do OTS during scheduled downtime

@tcaiger tcaiger changed the title feat(security): RN-1303: Update password storage feat(security): RN-1303: Update password storage to use argon2 Sep 6, 2024
@@ -66,7 +66,7 @@ const upsertApiClient = async ({
password: string;
salt: string;
}) => {
const secretKeyHash = encryptPassword(password, salt);
const secretKeyHash = await encryptPassword(password, salt);
Copy link
Contributor Author

@tcaiger tcaiger Sep 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the api clients start up, the api client hashes in the database are re-created so we don't need to manually migrate them. meditrak_app client is an exception to this since it does't have an orchestration server but since we know the password for it I am going to manually migrate its hash as a release step.

Copy link
Collaborator

@rohan-bes rohan-bes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this all checks out, nice work @tcaiger 🙏

@@ -53,8 +53,9 @@ export async function changePassword(req, res, next) {
throw new FormValidationError(error.message, ['password', 'passwordConfirm']);
}

const passwordAndSalt = await hashAndSaltPassword(passwordParam);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this, it's nice to break this out as it was a bit opaque before.

Could even do something like:

Suggested change
const passwordAndSalt = await hashAndSaltPassword(passwordParam);
const { password, salt } = await hashAndSaltPassword(passwordParam);
await models.user.updateById(userId, {
password,
salt,
});

Comment on lines +18 to +23
const verified = await verifyPassword(
secretKey,
process.env.API_CLIENT_SALT,
apiClient.secret_key_hash,
);
return verified ? apiClient?.getUser() : undefined;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could throw an error if no apiClient is found?

Suggested change
const verified = await verifyPassword(
secretKey,
process.env.API_CLIENT_SALT,
apiClient.secret_key_hash,
);
return verified ? apiClient?.getUser() : undefined;
if (!apiClient) {
return undefined;
}
const verified = await verifyPassword(
secretKey,
process.env.API_CLIENT_SALT,
apiClient.secret_key_hash,
);
return verified ? apiClient.getUser() : undefined;

Comment on lines +23 to +25
* Attempts to verify the password using argon2, if that fails, it tries to verify the password
* using sha256 plus argon2. If the password is verified using sha256, the password is moved to
* argon2.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comments 🙏

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.

2 participants