Creating a validator to check for common passwords in ASP.NET Core Identity

In my last post, I showed how you can create a custom validator for ASP.NET Core. In this post, I introduce a package that lets you validate that a password is not one of the most common passwords users choose.

You can find the package on GitHub and on NuGet, and can install it using dotnet add package CommonPasswordValidator. Currently, it supports ASP.NET Core 2.0 preview 2.

As Jeff describes in the appropriately named article Password Rules Are Bullshit, password rules can be a real pain. Obviously in theory, password rules make sense, but reality can be a bit different. The default Identity templates require:

Passwords must have at least one lowercase ('a'-'z')

Passwords must have at least one uppercase ('A'-'Z')

Passwords must have at least one digit ('0'-'9')

Passwords must have at least one non alphanumeric character

All these rules will theoretically increase the entropy of any passwords a user enters. But you just know that's not really what happens.

All it means is that instead of entering password, they enter Password1!

And on top of that, if you're using a password manager, these password rules can get in the way. So your 40 character random password happens to not have a digit in this time? Pretty sure it's still OK... should you really have to generate a new password?

Check for common passwords - There's plenty of stats on the terrible password choices user make to their own devices, and you an create your own by checking out password lists available online. For example, 30% have a password from the top 10,000 most common passwords!

In this post I'll describe a custom validator you can add to your ASP.NET Core Identity project to prevent users using the most common passwords

Check for basic entropy - Even with a length requirement, and checking for common passwords, users can make terrible password choices like 9999999999. A simple approach to tackling this is to require a minimum number of unique digits.

In ASP.NET Core Identity 2.0, you can require a minimum number of required digits using options.Password.RequiredUniqueChars = 6

Check for special case passwords - User's shouldn't be allowed to use their username, email or other obvious values as their password.

Whether you agree 100% with these rules doesn't really matter, but I think most people will agree with at least a majority of them. Either way, preventing the most common passwords is somewhat of a no-brainer.

There's no built-in way of achieving this, but thanks to ASP.NET Core Identity's extensibility, we can create a custom validator instead.

Creating a validator to check for common passwords

ASP.NET Core Identity lets you register custom password validators. These are executed when a user registers on your site, or changes their password, and let you apply additional constraints to the password.

In my last post, I showed how to create custom validators. Creating a validator to check for common passwords is pretty simple - we load the list of forbidden passwords into a HashSet, and check that the user's password is not one of them:

This validator is pretty standard. We have a list of passwords that you are not allowed to use, stored in the static HashSet<string>. ASP.NET Core Identity will call ValidateAsync when a new user registers, passing in the new user object, and the new password.

As we don't need to access the user object itself, we can make this validator completely generic to TUser, instead of limiting it to IdentityUser<TKey> as we did in my last post.

Each of these passwords lists is stored as an embedded resource in the NuGet package. In the new .csproj file format, you do this by removing it from the normal wildcard inclusion, and marking as EmbeddedResource:

With the lists embedded in the dll, we can simply load the passwords from the embedded resource into a HashSet.

Loading a list of strings from an embedded resource

You can read an embedded resource as a stream from the assembly using the GetManifestResourceStream() method on the Assembly type. I created a small helper class that loads the embedded file from the assembly, reads it line by line, and adds the password to the HashSet (using a case-insensitive string comparer).

With the validator in place, if a user tries to use a password that's too common, they'll get a standard warning when registering on your site:

Summary

This post was based on the suggestion by Jeff Attwood that we should limit password composition rules, focus on length, and ensure users can't choose common passwords.

ASP.NET Core Identity lets you add custom validators. This post showed how you could create a validator that ensures the entered password isn't in the top 100 - 100,000 of the 10 million most common passwords.

You can view the source code for the validator on GitHub, or you can install the NuGet package using the command

dotnet add package CommonPasswordValidator

Currently, the package targets .NET Core 2.0 preview 2. If you have any comments, suggestions, or bugs, please raise an issue or leave a comment! Thanks