docs.rodeo

MDN Web Docs mirror

Passwords

The original authentication method on the web, and still the most common, is the password.

In this guide we’ll:

Finally, we’ll discuss how even with the best practices, password-based authentication should be considered a relatively weak form of authentication, and should be supplemented with other methods or replaced entirely, if possible.

Overview

To provide password-based authentication, a website implements two main flows: registration and signing in.

When the user registers:

  1. The user supplies a new username and password, for example by entering it in a {{htmlelement("form")}}  element in the website.
  2. The web page sends the username and password to a server, for example, by submitting the form data in a {{httpmethod("POST")}}  request.
  3. The server creates a new record for this user in its database. The key is the username and the password is stored under it.

Registration using a password.

When the user signs in:

  1. The user supplies the username and password.
  2. The web page sends the username and password to the server.
  3. The server retrieves the stored password for the user, and compares the stored password with the one it just received.
  4. If the passwords match, the user is signed in.

Signing in using a password.

Attacks and defenses

Looking at this overview, we can see some of the ways an attacker can impersonate the user.

Defenses

In the next three sections we’ll take a more detailed look at the main flows needed for a password-based authentication system:

In each section, we’ll highlight practices that help to minimize the threat posed by the attacks listed, but as we’ll see, it is impossible to eliminate them completely.

Registration

In registration, a new user supplies a new username and password. The site is very likely to ask for an email address as well, and may choose to use the email address as the username.

The site should ask for this information using an HTML {{htmlelement("form")}} .

Form design

Well-designed forms help users work with passwords effectively, and also help password managers integrate with a site.

Typically, on a registration form, a password manager will:

Following the practices below helps password managers recognize forms they need to interact with, the elements they contain, and the points at which they need to be involved.

For more information, see:

The registration form typically asks the user to enter the password twice.

Form submission

When the user submits the form, the website front end sends the username, both copies of the password, and the email address to the server, using an HTTP {{httpmethod("POST")}}  request. This must take place over {{glossary("HTTPS")}} , to prevent attackers from intercepting the password in transit.

Username and password validation

When the server receives the POST request, it validates the username and password. The username must not match an existing username, and the copies of the password must match each other.

The risk of guessing attacks can be reduced if users choose stronger passwords, and the policies websites follow can help with this.

When users choose new passwords, websites should:

Additionally, websites can:

Note, though, that this is far from a complete defense for these attacks: for example, data breaches may not be public, or may happen after the password was chosen.

Websites should also consider using a password strength tool like zxcvbn: note that this particular tool also checks passwords against the Have I Been Pwned data.

For more information, see:

The client may also validate data before sending it to the server, but this is only as a convenience to users: the server must still validate the data as well.

Storing passwords

If any errors occur the server responds with an error message. Otherwise the server stores the password as a record in its database, keyed by the username.

Hashing passwords

Websites must not store passwords in {{glossary("plaintext")}}  form. Instead, when the user registers with a new password (or changes their password), the password is hashed and the hash is stored. When the user presents their password on login, the site:

A hash is a one-way function, meaning that it’s not possible to derive the original input to a hash function from its output.

This means that if an attacker gets access to the database, they will typically try to extract passwords by hashing lists of common passwords and comparing the results with the entries in the database. For this reason the hash functions chosen for password storage are intentionally slow and difficult to optimize.

Hash functions that are designed for hashing passwords typically allow you to configure the amount of work involved to create the hash, so they can be made slower or faster depending on the expected capabilities of the attacker.

Precomputed hash tables

Rather than calculate hash tables themselves, attackers can look up the password corresponding to a hash in a precomputed table (also known as a rainbow table) mapping possible passwords to their hashes:

Password Hash
pa55w0rd 56965E2A…
abcdef BEF57EC7…
letmein 1C8BFE8F…

Although these tables may be very large, such attacks can be effective because table lookup is a fast operation.

Salt and pepper

To defeat attacks that use precomputed hash tables, salt must be added to the password before it is hashed. Salt is a random value unique to each password. It does not have to be secret: salt is stored alongside the hashed password. However, it prevents an attacker from using precomputed hash values, because the salt means that a given password will hash to a different value.

As an additional defense, websites may also add pepper to the hash function’s input. Unlike salt, pepper is:

Hashing algorithms

Websites should use standard algorithms to hash passwords. These algorithms support all the features discussed above. The OWASP guide to password storage recommends, in order of preference:

  1. Argon2id
  2. scrypt
  3. bcrypt
  4. PBKDF2

Using web frameworks

Password storage and verification functions are difficult to implement securely, so you should use functions provided by a reputable framework, rather than trying to implement your own. For example, Django uses PBKDF2 by default but enables you to use a different algorithm if you choose.

Email verification

If the website is intending to use email in the password reset flow, then the server must also check that the email address belongs to the user signing up. To do this, the server typically generates a random token and sets it as a parameter to a verification URL:

https://example.org/verify?<random-token>

The server then sends an email to the address the user gave. The email asks the user to click a link to the verification URL. The page can then extract that token and use it to find the user’s record in the database. It can then mark the email address as verified.

Login

To sign in, the user enters their username and password using an HTML <form> dedicated to sign-in.

Just like the registration form, the login form should be designed to work with (and be tested to work with) password managers. To do this, the form should follow the practices previously described in Form design.

When the user submits the form, the website front end sends the username and password to the server, using an HTTP POST request. Again, this must take place over TLS, to prevent attackers from intercepting the password in transit.

When the server receives the POST request, the server:

If the comparison succeeds, the server signs the user in and returns success.

If the record was not found or the comparison fails, the server must return the same error message in both cases. Otherwise, attackers can determine whether an account exists and can use this information to execute further attacks.

Password reset

The password reset flow enables a user to reset the password, when they have forgotten or lost it. This usually relies on the user having supplied (and then verified) their email address when they registered.

When the user asks to reset their password, the website asks the user to enter their email address. The website may ask the user to solve a CAPTCHA at this point, to make it harder for a malicious third party to spam a legitimate user with multiple password reset requests.

The website back end then checks whether it has a record for this email address. Whether or not it does have a record, the website gives the same message to the user: that it has sent an email to the address given, with further instructions. Providing the same message in both cases prevents an attacker from finding out whether a given email address is associated with an account: this information could be used in further attacks (such as targeted phishing, or spearphishing, attacks).

When the user clicks the link, the reset page extracts the URL parameter and looks for a matching stored reset token. If a reset token is found and has not expired, the website allows the user to enter a new password. This flow follows similar rules to the registration form, to enable the new password to be recognized by a password manager.

Finally, the website emails the user confirmation that their password has been changed.

For more information, see:

Weaknesses of password-based authentication

The practices described above help reduce the risks of a password-based authentication system, but passwords remain an inherently vulnerable authentication method:

To address these weaknesses, consider using alternative methods, either instead of passwords or as {{glossary("multi-factor authentication", "additional authentication factors")}} . For example, websites sometimes use passwords with a one-time password as a second factor, and some websites support passkeys, which are resistant to phishing attacks.

In this article

View on MDN