Weak password recovery
Recovering forgotten passwords is not as straightforward as it sounds!
Select your ecosystem
What is weak password recovery?
Weak password recovery is when an application allows an attacker to reset the password of another user without their consent.
The most common form of password recovery is the classic "forgot password" flow that you have likely used before, where the application will send you an email with a link containing a one-time code, and that link allows you to choose a new password. Security issues in this flow can often result in vulnerabilities that allow an attacker to take full control of a victim's account.
About this lesson
In this lesson, you will learn about vulnerabilities stemming from weak password recovery flows and how to protect your applications against them. We will step into the shoes of Roger, a pentester/hacker who has a bad relationship with his neighbor Karen, and seeks revenge by exploiting a weak password recovery flow to cancel her electricity service.
"That's it," said Roger, "I've had enough!" He saw Karen as he drove into his driveway after a hard day of pentesting. She was hanging up even more lights. You see, Karen was convinced that someone was going to steal the geraniums from her front garden at night, so she installed a set of 14 motion-activated flood lights pointed into her front yard, across the street, and directly into Roger's bedroom window.
At random intervals throughout the night, a slight gust of wind would blow a tree branch โ or a worm would squirm in the grass โ activating the powerful lights and interrupting Roger's sleep. Asking nicely was futile, as she felt the safety of her geraniums far outweigh his need for an evening's rest. He was at the end of his tether.
Roger smiled as he started to formulate a cunning plan.
The vulnerability above is quite simple, the password recovery token was not random. Rather, the recovery token was just an MD5 hash of the user's email address. Anyone can easily generate an MD5 hash of a known value, which means that an attacker can easily generate a password reset token for any account, as long as they know the account's email address. The underlying vulnerable code might look something like this:
There are plenty more ways that a password recovery flow can be vulnerable, here are some examples:
- The password token is revealed somewhere in the password recovery flow before the email is sent
- The hostname in the link that is sent in the password recovery email can be altered by the attacker (for example, through host header injection)
- Multiple emails can be injected into the password recovery email field, causing a user's password reset token to be sent to an attacker's email address
- Password tokens are not random, unique, or long enough
- Password tokens don't expire after use
- Parameter pollution is possible in the email field
- Different responses are shown (or different amounts of time are taken) for requests to existent and non-existent accounts, meaning that the password recovery functionality can be used to enumerate accounts
What is the impact of Weak password recovery?
The highest severity impact of a weak password recovery flow would be full account compromise of all user accounts. The business impact for this outcome will depend on what the application is used for, but is likely to be critical. It is also important to consider the fallout that would occur once the account(s) have been compromised. For example:
- The attacker may be able to scrape PII or sensitive data from a victim's account dashboards
- The attacker may gain access to a highly-privileged account, allowing additional functionality to stage new attacks
- The business' finances and processes will be impacted as it recovers from account compromises
In order to mitigate the reset vulnerability in the code above, we need to implement a cryptographically strong, randomly generated password reset token that changes on every password reset request and expires after 1 use (or a set time).
The code might look something like this:
In this instance, we've used the UUID NPM package to generate the token. It's important to note that the underlying PRNG is extremely important if you decide to use UUIDs for security purposes. Be sure to use one that is advertised as cryptographically strong, this one is!
Don't reinvent the IAM wheel
Password recovery flows are notoriously difficult to get right. For this reason, it's usually best to use a reputable existing solution, such as a third-party IAM solution or the authentication boilerplate that is included with a popular framework. If this is an option for you โ it's usually the easiest and most secure solution!
Creating your own secure password recovery flow
If using an existing solution is not an option for you, it's important to know how a secure password recovery flow operates. A typical password reset flow will work something like this:
- User submits a "forgot password" form containing their username or email address
- A cryptographically strong, randomly generated UUID is assigned to the user as their reset token, and stored in the database
- The user is then sent a password recovery request email containing the reset token embedded in a link
- When the user clicks that link, they send the token along with it. The token would be checked against the current token associated with that user. If it matches, and is currently valid, the user will be able to change the password.
- Once the token is used, or if it has not been used for a set amount of time (ex: more than an hour), it expires permanently
Keep learning
There is a lot to learn about passwords and cryptography. Check out some of these links:
- Take a look at our lessons on insecure randomness and insecure hash
- Learn more about this CWE for weak password recovery
- Check out OWASP and their recommendations for password resets
- And of course, there is always a cheat sheet!