What is a secure and efficient method for website users to reset their password? - email

Many sites implement different methods and I am having a hard time deciding on which method would work best for my site.
My user profiles contain the following data:
username
password (in hash/digest form)
email
I'd like the password reset method to be secure, user-friendly, and efficient.

You should add two fields, reset_code and reset_expiry
This is the process for a secure password reset functionality.
User selects "Forgot password".
User prompted for email/username.
If valid, generates a GUID, and stores it in reset_code and also stores Now()+24 hours in reset_expiry in the database against that particular user.
Then it sends an email to the email address with a link to confirm the password reset. This email would contain a link to your website with the user's username AND reset_code embedded. (This stops a malicious user resetting a third parties password just by knowing their email)
Once the user clicks on the link in the email, they will be directed to your website.
Your website will validate that: the username and reset_code matches, and the current time hasn't exceeded the reset_expiry time.
If all is okay, we can complete the password reset. This can be done by either:
a) Onscreen a new randomly generated password
b) A new randomly generated password via email
c) The ability to enter a password of his/her own choosing

You should not store the passwords of your users, not even in encrypted form. You should only store the hash/digest necessary for authentication. Then, you can't "recover" the password (because you don't know it), you can just reset it, and/or give the user a temporary one-time password that allows him to set a new password.
Update: if you are doing the above, the standard procedure is to have a "require-password-reset" form. The user enters his id (typically his email) and a "token" (eg, a random string) is generated, stored in some table with some expiration date, and sent to his email along with a link to the "password-reset" form. In this form the token is checked, the user is allowed to enter a new password, and instructed to attempt a new login.
Update 2: A small privacy issue might arise: What should we do if the user id (email, user name, or whatever) entered in the request form does not exist in our database? To output a message "User does not exist. Check the id and retry." may be ok, but in some cases it would cause a privacy problem: anyone can check if other someone is registered in your database! If you want to avoid that, you must output the same message ("a mail has been sent with instructions...") even if the user wasnt found (and hence a mail was not actually sent).
Similar privacy issues advise to just output the message "login incorrect : bad user or password" when the user tries to login unsuccessfully - don't disclose if it was an incorrect user or password.

I agree with Leonbloy. Storing the password leads to trouble like the Gawker incident from a few weeks ago (1.5 million userid/pwd combinations were discovered and published).
You should, however, have a "reset password" function that e-mails the new password to the original e-mail address used to open the account.
There should be no provision for changing the e-mail address during the password reset. If the user doesn't have access to the old e-mail account anymore, too bad. Abandon the account and start over.
And use a good Captcha on the reset screen.

Related

Can you send a link to the specific page where a user resets their password via Keycloak's API?

We have a PHP/MySQL based User Management System and are integrating it with Keycloak version 16 where we will store users credentials.
Our application does not allow users to self register. We create user accounts on the system. When we do this we do NOT specify a password because we want users to set up their own password.
The current system sends 2 separate emails in 2 different circumstances regarding passwords:
If it's a completely new user who does NOT have an existing password, we send them a link to set up a password.
If it's an existing user who already has a password, the system allows them to reset it, e.g. if they forget their password and can't login.
Keycloak seems to cater for scenario (2) because the login forms have a forgotten password link which opens a form where the user can enter their email address and receive a link which lets them do (2).
Unfortunately it doesn't deal with scenario (1) very well and that's where our problem starts. This has been asked a while ago Send password forgotten mail but it seems that Keycloak didn't support this very well in 2020 and perhaps still doesn't now.
Our "workaround" to this was that we added custom email templates and a custom page (reference: Themes on https://www.keycloak.org/docs/latest/server_development/#emails) which includes wording that caters for both scenarios, e.g. "set your password" rather than "reset your (existing) password". The result of this is that our email and form now reads appropriately for both scenarios (1) and (2).
The problem
We want to be able to send a link to the user that allows them to set their initial password to cover scenario (1).
We know that this page exists because on the login page for Keycloak there is a link to the forgotten password form that handles scenario (2). However, the form requires the user to enter their email address and submit the form. The user then receives an email from Keycloak which contains a URL to the page where they can do this. The URL has the following format:
https://example.com/auth/realms/foo/login-actions/action-token?key=...
The key= contains a ~945 character token. Going to the URL above redirects to the form where the user can reset their password. This next URL does not contain a token but a cookie has been set in the browser - by the previous URL - which makes it functional:
https://example.com/auth/realms/foo/login-actions/required-action?execution=UPDATE_PASSWORD
We can't send either of these URLs to the user because the first one (containing key=) has no API method for us to find out what it is - it's only possible to generate this by going through the "forgotten password" step during login, in the browser.
The second URL (/login-actions/required-action...) won't work either because it relies on the previous URL (containing key=) setting the cookie in the browser. If you try and go to this second URL directly (i.e. bypassing the first URL) it will error.
So neither of these URLs will work because we can't find what the first one is programmatically, and we can't use the second one without knowing the first one.
I found https://lists.jboss.org/pipermail/keycloak-user/2018-October/015910.html and the suggestion is using the Keycloak API to trigger a password reset email. This works - sending an HTTP PUT request containing 'UPDATE_PASSWORD' along with the relevant user ID sends the user an email. The request endpoint has the format PUT /{realm}/users/{id}/execute-actions-email which is documented on the link above.
Up to here all is fine - the user gets an email. However, this email does NOT contain a link that goes directly to the "reset password" page! Instead it sends them an email containing the following text:
Your administrator has just requested that you update your account by performing the following action(s): Update Password. Click on the link below to start this process.
Link to account update
When the user clicks "Link to account update" it then shows them a web page like this:
It is only when they click on the link on this page (the one that says "click here to proceed" on the screenshot) that they arrive at the form where they can reset their password.
This is a really poor user experience because the user gets sent a (badly worded) email with a link to... a page with another link! It should just take them to the password reset page directly. What's more frustrating is the fact that Keycloak is clearly capable of generating/sending the exact email we'd like in this scenario: the one which gets sent when a user manually does a password reset via their browser.
So the problem seems that Keycloak's API doesn't support this incredibly important and common use-case of a user being able to set an initial password, in a user-friendly manner.
I am adding the js script in the template to automatically click "click here to proceed". It's ugly but at least the user doesn't see the page

Password reset via mail

I am doing a web application using Eclipse EE. I have a module which is supposed to reset user passwords and sends the user (via email) a link to a page where they can reset the password. Any insights please?
The main steps would be:
Provide a "forgot password" page where the user can enter her email address
look up the entered address but don't tell the user any details. Don't output a message like "email address not found" as it would allow a malicious user to look up valid email addresses. Better give them a message like "you'll receive a link to reset your password if the entered email address is registered".
Create a strong unique random identifier to associate it to that reset-request. E.g. use a cryptographically RNG provided by the framework and combine it with something unique like e.g. a GUID.
Store that unique identifier along with that reset-request in e.g. a table in your database and put a timestamp to that record. That's important as you'd want the user to be only able to reset her password within a fixed time frame.
Send an email with that unique identifier put as a query parameter in a link to your application. E.g. https://yourapp.com/pwreset/?requestID=7392af1747ce3781
Fetech the requestID parameter at your pwreset-controller and look it up in your database. If the request comes in within a reasonable time frame based on the initial request timestamp, then let the user reset her password.
Delete successful request-records from your database, so that a sent pw reset link can only be used once. And periodically delete expired records.
Use this as a starting point. As I'm no crypto expert, you may one have a look at this approach ;)

Best practices for forgot password function via REST API

I am trying to find the best practices for forgot password functionality via sending a link to reset password i.e. sending an email with a one time token to the registered user. The token will be stored in the database and when the user clicks the link, we check the token and allow the user to set a new password.
Best practices while designing forgot password function -
The token must be unpredictable, that's accomplished best with a
"really" random code which is not based upon a timestamp or values
like the user-id.
Like a password, the token should be hashed, before storing it in
the database. This makes them useless for an attacker, even if the
database is stolen.
The reset-link should preferably be short to avoid problems with
email clients, and contain only safe characters 0-9 A-Z a-z
(base62 encoded)
The token should have an expiration time within single-digit hours.
The token should be marked as used,after the user has
successfully set a new password.
When a user changes their password or requests another password
reset, expire all tokens already associated with their account.
These are some of the points I found. What can be other security issues that should be considered ?
Sources:
Secure password-reset function
Ycombinator News
A couple other practices I've seen:
Check user is on the same machine/browser/IP as the one where the reset password request was triggered (unless it was initiated by admin/system).
Rate-limit number of reset tokens that can be generated for an account.
It should also be noted that the best practice is usually to use an established library rather than inventing your own mechanism, as too many things can be overlooked.
I have the same question and found the OWASP Forgot Password Cheat Sheet.
Also few things that I would like to add:
Usually if user entered non existing email sites anyway shows message "pwd restoration link was sent". This is due to prevent hackers from determining that user with the email exists in system. But IMHO it's better to say user that email is not exists because usually it may not remember email used during registration.
It is better to add some additional personal question to user like a birthday date. If hacker stole user's email it makes harder to receive reset link. But since reset link may be sent to user by site admin the question with birthday must be on change password page which is opened by link.
Hackers may automatically send a lot of letters to some user. Some sites uses a CAPTCHA near email field to prevent this.
After successful changing of password all active sessions should be closed and user must be logged out. Thus even if hacker is logged in he will logged out.
It is a good idea to hash a restoration ticket like a password. Here should be used the same hashing algorithms like with password: Argon2, SCrypt, BCrypt.
After user restoration password it is good to mark it a possible fraud and for some time (like a week) do not allow to make some critical actions, like withdrawal money from account.
Also some sites are sending a letter to user that it's password was changed. They do this when user was logged normally changed it manually but maybe it is good to send the same latter when pwd was reseted.

Best practice for resetting forgotten user passwords

As far as I can think, there are two reasonable ways to reset a user's forgotten password.
Have the user enter their email address and a new plaintext password is sent to their email address.
A link is sent to their email address which has a UID number in the URL. Clicking on this takes the user to a form on the website where they can choose there own new password.
Which method is preferable and why?
If method 1 is used, perhaps a third party could read the email and obtain the new password.
If method 2 is used, what is to stop someone methodically going through UID codes to try and access the form to change a user's password?
The best pattern would be :
User requests password reset. Best is to do it through username, and
don't indicate if the username exists or not (to avoid possible
users listing through a script)
You generate a record in a new database table with userid, datetime
of request (= current datetime), and a GUID you just generated
You send a mail to the user, pointing to password reset page with
the GUID (not the userid) as parameter
On this page, you should check that the GUID is existing, and
eventually you could put some expiration date (=the user has 1 day
to reset, for example)
Don't forget to mark the record as "used" (with an extra field in
the table) when the user reset his password, so that you can stop an
eventual second try...
It could possibly even more secure, but that is already quite good I think....
OWASP has a good checklist of https://www.owasp.org/index.php/Forgot_Password_Cheat_Sheet
Here is a quick summary of steps:
Gather Identity Data or Security Questions
Verify Security Questions
Send a Token Over a Side-Channel
Allow user to change password
Verify if the sender is the real user by asking verification questions.
Do not send password on the personal email that is not on the planate employees list.
Do not add the word "password" to the title or body of the email.
Make sure to send separately the username and password.
For Office 365 user, direct them to the forgot my password area or send this link https://passwordreset.microsoftonline.com
Don't get intimidated by the user, escalate to IT manager if the need arises.

Handling users that exist, but try to login through Facebook OAuth / etc

I've got a decent set of existing users on my website who login via their emailaddress as their username.
I'm setting up Facebook OAuth mechanism to allow new users to sign up more conveniently, but I'm not sure how to handle the scenario when a user who already has an email address registered with our system and now tries to login via Facebook.
Should I consider him the same user?
Should I treat him like a new user?
The situation is more complicated by the fact that I dont validate their email addresses (when they login directly into my system), so i cant assume they are the same user.
How do others resolve this conflict, or do other folks simply treat this user connecting via FB as a new entity?
On your login screen, users can have a choice: you may put
new user: signup using facebook
since this is a totally new user account, then you just need to do your facebook connect + request for email permission, etc.
existing user: login by email
Once they do this, let them login using the old-fashioned way. Then once signed in, prompt them to connect this email address to their facebook account. So the flow is login via email then optional facebook connect.
To do this, I assume you've added a field on your database table for user_accounts, that is facebook_user_id or fb_id or user_id, etc. Then on facebook connect, get the logged-in-email, UPDATE table SET fb_id = xxx WHERE email = xx
I've pondered the same issue. I think we'll go with the verified email (Facebook Connect) getting attached to and logged into the existing account with the same email.
Before we connect and log them in we'll explain that the account exist and ask for their password (they signed up using email and password, so they should know it) to ensure it's the right person.
If you support multiple external authentications (Google OAuth, Facebook etc) then you may not have a password, and in that case it gets slightly trickier.
If you do log them in to the existing account without asking for a password, make sure you clear existing sessions to avoid 'anticipation attacks', where an attacker anticipates that the target signs up, creates an account and keep the session after they've signed up and attached to the attackers existing account.