Introduction
Password Validator is an assembly (or two) that assists in validating passwords in .NET applications.
The main assembly does the actual validation, and there is a second assembly for Web applications that makes it easier to add validation to your ASP.NET MVC applications.
Background
I was looking for a way to validate passwords for an upcoming project because we need to do that better than we are, so I was looking for some .NET library to do that. Unfortunately, Google only turned up a ton of regular expressions which is all fine an well, but it wasn’t really what I was looking for. I really wanted to be able to check against a dictionary of potential passwords and have it reject a password because it was in the list and it also had to check against the regular rules of “must has a number”, and so on.
So, I created Xander.PasswordValidator.
The idea is that you can create a Validator object which can then be used to evaluate the suitability of potential passwords. When creating the validator you can pass in settings from code, or have it read settings from a config file. You can extend the functionality, so if there is something that you want to check that I’ve not thought about you have points where you can extend that. If the lists of forbidden passwords are not suitable then you can add your own lists, if the rules for checking against those lists are not suitable, then you can add your own ways to check.
Also, if you write web applications using ASP.NET MVC then there is a really simple attribute you can apply to a property in your model to have the validator check the value.
Getting Started
The assemblies are up on NuGet so it is very easy to get going. However, if you want to build the assemblies yourself then the source code is available on GitHub with an MIT License.
![Xander.PasswordValidator on NuGet Image 1](/KB/validation/566772/NuGetPackages.png)
Using the basic Validator
At its core is the Validator
class. It performs the validation of the password and returns a Boolean to the caller to let them know if the validation passed or failed.
The Validator
can take settings set by the caller, or it can find settings in the application’s configuration file.
Here is a simple example of it working:
var settings = new PasswordValidationSettings();
settings.MinimumPasswordLength = 6;
settings.NeedsLetter = true;
settings.NeedsNumber = true;
settings.StandardWordLists.Add(StandardWordList.MostCommon500Passwords);
var validator = new Validator(settings);
bool result = validator.Validate("MySuperSecretPassword");
First off, a settings class is created, then various options are set. If you don’t set any options then the validator allows any password.
In this example the settings mandate the a password must be at least six characters (MinimumPasswordLength
), it must have a letter (NeedsLetter
), it must have a number (NeedsNumber
), and it must not appear in the built in list of the most common 500 passwords (StarndardWordLists.Add(StandardWordList.MostCommon500Passwords
).
Then the Validator
is created and passed the settings that we’ve prepared.
Finally, the Validate
method is called passing in the password that is to be validated. The result indicates whether the password passed or failed (in the example above, it failed as it does not contain a number).
Settings from the config file
If you prefer to have the settings for the validator in the config file then you can instantiate a Validator
without passing anything to its constructor and it will use the settings in the config file instead.
It should go without saying that you should only put the settings in the config file in a secure environment. And in any event certain configuration elements are not available in the config file at all, such as the ability to specify your own validation checking routines.
To use settings in the config file you must set up a the section where the settings will go, and then create the section with the settings in it.
To define the section:
<configSections>
And an example of the configuration section itself:
<passwordValidation>
<rules minimumPasswordLength="6" needsNumber="false"
needsLetter="false" needsSymbol="false">
<wordListProcessOptions checkForNumberSuffix="true"
checkForDoubledUpWord="true" checkForReversedWord="true" />
<standardWordLists>
<add value="FemaleNames"/>
<add value="MaleNames"/>
<add value="MostCommon500Passwords"/>
<add value="Surnames"/>
</standardWordLists>
<customWordLists>
<add file="WordLists/MyCustomWordList.txt" />
<add file="WordLists/MyOtherCustomWordList.txt" />
</customWordLists>
</rules>
</passwordValidation>
The rules element
The rules section defines the actual rules by which the passwords will be validated.
<rules minimumPasswordLength="13" needsNumber="true" needsLetter="true" needsSymbol="true">
minimumPasswordLength
: a positive integer that defines the minimum number of characters needed for a valid password. It is optional and if missing will default to 8
. needsNumber
: Boolean that indicates whether the password needs a number in it. It is optional and if missing will default to true
. needsLetter
: Boolean that indicates whether the password needs a letter in it. It is optional and if missing will default to true
. needsSymbol
: Boolean that indicates whether the password needs a symbol in it. It is optional and if missing will default to false
.
Rules can have a number of child elements also.
wordListProcessOptions
: A set of options for how the word lists are processed. standardWordLists
: A collection of built in word lists to use to check the password against. customWordLists
: A collection of custom word lists to use to check the password against.
The word list process options
By default, checking the password against the word lists only checks to see if the password is in a word list. These are additional options for checking against the word lists.
<wordListProcessOptions checkForNumberSuffix="true"
checkForDoubledUpWord="true" checkForReversedWord="true" />
checkForNumberSuffix
: Indicates whether the password should be checked to see if it is simply in the word list with an additional digit appended. This is optional, and by default is false
. checkForDoubledUpWord
: Indicates whether the password should be checked to see if it is the same sequence repeated over again, and if it is to see if the first half is in the word list. This is optional and the default value is false
. checkForReversedWord
: Indicates the reversed form of the password should be checked to see if it is in the word list. This is optional and the default value is false.
Standard word lists
This element is a container for a collection of standard word list items.
<standardwordlists>
<add value="FemaleNames" />
<add value="MaleNames" />
<add value="MostCommon500Passwords" />
<add value="Surnames" />
</standardwordlists>
The valid words list are:
Custom word lists
This element is a container for a collection of file paths to plain text files that contain custom word lists to check against. A word list file is simply a plain text file with one word per line.
<customWordLists>
<add file="WordLists/MyCustomWordList.txt" />
<add file="WordLists/MyOtherCustomWordList.txt" />
</customWordLists>
The paths are relative to the working directory of the application in which the password validator is operating. In an ASP.NET web application the paths should be prefixed with ~ to ensure they are correctly mapped on the server relative to the root of the web application. (See the next section on using Xander.PasswordValidator in a web application.)
Using Xander.PasswordValidator in a web application
As you will have noticed above, there are two NuGet packages. Using Xander.PasswordValidator in an MVC application requires the use of the second package, the Xander.PasswordValidator.Web NuGet package, if you want to use attribute decoration in your model properties. This package depends on the first package so you only need to tell NuGet to install this package in your application and it will automatically get the first one as well.
At its simplest, all you need to do is to decorate a property in your model with the PasswordValidationAttribute
, like this:
public class SomeModel
{
[PasswordValidation]
public string Password { get; set; }
}
That will validate the password based on the settings in the config file.
If you want to use custom word lists there is one additional step that you need to take.
Registering the Password Validator
In order for the file paths to the custom word lists to be resolved correctly in a web application you need to register the validator in the Application_Start()
method in your web application’s HttpApplication
derived class (or anywhere before first use).
For example, the Application_Start()
method may look like this:
protected void Application_Start()
{
PasswordValidatorRegistration.Register();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Validating with settings from code in a web application
As the settings can get quite complex they cannot be set directly in the attribute that you use to decorate the model. Instead they can be set elsewhere and referenced in the attribute.
The settings can be configured as normal then added to the PasswordValidationSettingsCache
. For example:
var settings = new PasswordValidationSettings();
settings.NeedsNumber = true;
settings.NeedsSymbol = true;
settings.MinimumPasswordLength = 6;
settings.StandardWordLists.Add(StandardWordList.FemaleNames);
settings.StandardWordLists.Add(StandardWordList.MaleNames);
settings.StandardWordLists.Add(StandardWordList.Surnames);
settings.StandardWordLists.Add(StandardWordList.MostCommon500Passwords);
PasswordValidationSettingsCache.Add("StandardRules", settings);
This code would typically be placed in, or called by, the Application_Start()
method, after registering the password validator.
The important line is the last one. It adds the setting to the cache with the name StandardRules
that can then be referenced in the attribute later. Like this:
public class MyModel
{
[PasswordValidation("StandardRules")]
public string Password { get; set; }
}
The PasswordValidationAttribute
references the entry in the cache, which is then retrieved to perform the validation.
You can also include the standard ErrorMessage
parameters into the attribute so that the message that is displayed is customized to your application. For example:
public class MyModel
{
[PasswordValidation("StandardRules",
ErrorMessage="Passwords must be at least 6 characters and contains numbers and symbols.")]
public string Password { get; set; }
}
Providing your own Validation Handler
If the password validator does not have the validation rules that you need for your project then it is easily extendable. You can create your own ValidationHander
derived classes and add them via the PasswordValdiationSettings
object that is passed into the Validator
.
ValidationHandler
The ValidationHandler
class is an abstract base class which is extended in the Xander.PasswordValidator
framework itself to provide the various built-in validation routines. (You can see examples of some of them in the downloadable demo project as well as in the source on GitHub.)
You can create your own by simply creating a class and setting Xander.PasswordValidator.ValidationHandler
as the base class and overriding the Validate()
method.
The Validate
method simply accepts the password as the parameter and returns a Boolean, returning true
to indicate that the password passes the validation, false
if it fails the validation.
For example, here is a very simple validator that rejects passwords that look like dates:
using System;
using Xander.PasswordValidator;
namespace Demo.ValidationHandlers
{
public class NoDatesValidationHandler : ValidationHandler
{
public override bool Validate(string password)
{
DateTime date;
var parseResult = DateTime.TryParse(password, out date);
return !parseResult;
}
}
}
To set this up so that the validator calls it, it needs to be added as part of the settings. You pass in the type of the handler. The validation framework will create an instance of the handler for you, if it needs it. If validation fails before it gets a chance to run your validator then your validator will not run.
var settings = new PasswordValidationSettings();
settings.CustomValidators.Add(typeof(NoDatesValidationHandler));
CustomValidationHandler<TData>
This derives from ValidationHandler
and is used when you need to pass some additional data or objects into your validation handler for it to work properly.
The Validate
method works as before, except you now have access to an additional property from the base class that contains your custom data, called CustomData
. CustomData
is an object passed in through the settings.
To pass in the data through the settings you use the CustomSettings
property on the PasswordValidationSettings
object. For example:
settings = new PasswordValidationSettings();
settings.MinimumPasswordLength = 6;
settings.CustomValidators.Add(typeof(PasswordHistoryValidationHandler));
settings.CustomSettings.Add(typeof(PasswordHistoryValidationHandler), new Repository());
The key passed into CustomSettings
is a type that refers to the ValidationHandler
type that the settings are to be sent to.
The custom ValidationHandler
looks something like this:
using System.Linq;
using System.Web;
using Xander.PasswordValidator;
namespace Demo.ValidationHandlers
{
public class PasswordHistoryValidationHandler : CustomValidationHandler<Repository>
{
public override bool Validate(string password)
{
var user = HttpContext.Current.User;
var history = CustomData.GetPasswordHistory(user.Identity.Name);
return !history.Any(h => string.Compare(password, h, true) == 0);
}
}
}
In the above example, the settings pass in a repository which is passed on to the ValidationHandler
when the Validator
is run. The repository is used to get a history of passwords (it is a dummy repository in this example – in real life you should never have access to plain text passwords like this) and the history can be checked against the current password to ensure that it does not match any of the historical passwords.
Custom processing of word lists
When word lists are processed, a regular expression is built in order to quickly traverse the lists. The regular expression is built using a number of builders which create various parts of the regular expression. One for checking the password itself, and another for testing the password against the list but modified by adding a numeric suffix. You can add your own by creating your own class derived from WordListRegExBuilder
and then adding it to WordListProcessOptions
.
WordListRegExBuilder
is an abstract base class and demands that the GetRegularExpression
method is implemented in any concrete derived class. It also has a method called RegExEncode
which takes a string and encodes it for use in a regular expression, escaping out all the special symbols used by the regular expression engine.
For example, say you want to validate the password against the list, but check also for a numeric prefix, you can create a class to build that part of the regular expression. That class would look something like this:
using Xander.PasswordValidator;
namespace Xander.Demo.PasswordValidator.Web.Mvc3.Helpers
{
public class NumericPrefixBuilder : WordListRegExBuilder
{
public override string GetRegularExpression(string password)
{
return "[0-9]" + RegExEncode(password);
}
}
}
And to use this in the validator, add it to the settings like this:
var settings = new PasswordValidationSettings();
settings.WordListProcessOptions.CustomBuilders.Add(typeof(NumericPrefixBuilder));
The Validator
will create a new instance of your class and run the GetRegularExpression
method. It will then incorporates that into the regular expression that it is building and tests word lists using it.
Demos
To go along with this article are two demos of the Password Validator in action.
The first is a rather simple console application that takes input from the keyboard and tells the user whether the validation passed or not. This can easily be modified so that you can try out various settings on your own.
The second is an MVC 4 application that shows the validator working in a web context.
More resources
History
- 10/Apr/2013: Initial version.