Enforce Password Complexity Policies
Build a component to accommodate changing requirements without having to recompile your code.
The days of security as a software-development specialization are behind us.
Today, every software developer must be security savvy and every application must be secure. Unfortunately, there is a weak link in most e-security today: the password. I won't get onto detail about the nature of these faults because most of us are all too familiar with them.
Security is a tricky area. As a developer, it's a good idea to mitigate security issues by using Windows integrated security, wherever possible. In most cases, Windows can handle your users' secrets better than custom solutions you write yourself. In those few cases where it is better to roll your own solution to a security issues, you need to bear in mind the significance of what you are undertaking. You are assuming responsibility for storing your customer's and/or company's secrets, and you need to take every precaution possible to ensure you do so in a safe, effective, reliable, and secure way. Shirking this responsibility can have disastrous consequences.
The password is often the weak link in your security, partly because users don't always take this aspect of security seriously enough. They choose passwords that are easy to guess, such as "password." Or, they use birthdays or other obvious passwords. In this article, I'll show you how to counteract several potential password hacks, including how to stop dictionary attacks, where a potential intruder tries to gain access to your network by throwing out a large number of words until he or she discovers a given password. This article will make that attacker's job more difficult by imposing some basic constraints on your users. For example, long passwords are harder to guess than short ones, and passwords that contain characters from several character classes—letters, numbers, and punctuation—are unlikely to be in the attacker's dictionary.
I'll cover the best way to implement these constraints in a moment. But first, there is a common pitfall you must be aware of. It is tempting to solve this problem by the enthusiastic method: Collect some requirements from the network guys down the hall, fire up Visual Studio, and start coding.
Using this approach makes it hard to account for the fact that policies change. In this scenario, you must recode your applications whenever the company's policies change. The enthusiastic method is the road to bugs and, possibly, a range of different exploitable vulnerabilities. Writing code flexible enough to handle most of the likely policy changes requires slightly more up-front effort, but is well worth it in the long run, in both business and security terms.
A better approach is to create a password-complexity component. This is a component you write once, test once, and use many times. Using the .NET Framework's configuration extensibility, you can build a component that lets you specify these policies in your application's configuration file, where they can be edited easily by an administrator without compiling any code. If you have multiple applications on a computer, you can specify them as machine-wide defaults, employing .NET's configuration inheritance to handle the exceptions.
Specify Your Policies
A good place to start is with the XML that specifies your policies:
<minAlphaChars value="2" />
<minLength value="8" />
<minNumericChars value="2" />
This XML specifies the four conditions that all passwords must meet. Passwords must contain at least two alphabetic characters, must be at least eight characters in length, must contain at least two numeric characters, and must contain at least one of the specified punctuation characters.
Note that these rules are all special cases of common functionality. They test a candidate password against a constraint. Representing each rule as a class is a good way to model the "special cases" part. The "common functionality" part presents you with a classic dilemma: Should you factor the common members into an abstract base class, from which rules inherit, or into an interface, which they implement? The approach in this article uses the latter because of the modest amount of code required to implement a rule. It also lets you design your component to duplicate little, if any, code.
The primary responsibility of this interface is to test the rule the implementation represents. It also has a secondary responsibility. If the candidate password fails, you need to display a message to the user that explains what happened, and why. All rules need to provide such a message (common functionality) and must do it in a rule-specific manner (special cases)—hence, the method's inclusion in the interface. The complete interface looks like this:
Public Interface IValidationRule
ReadOnly Property Description() As String
Function Test(ByVal Password As String) _
Next, you need to implement IValidationRule for cases where you need to enforce a minimum number of alphabetic characters (see Listing 1). A few features of the AlphaCharactersRule class are worth mentioning. First, its constructor accepts an integer representing the critical value stored in the rule's XML node. This initialization isn't part of IValidationRule, partly because each rule requires different values for its initialization, and partly because different principals access these two functions. You need to handle the construction of a rule with your component plumbing. However, using rules to validate a candidate password is a function of application code.
Second, the Description property returns a string that starts with "must contain at least?" Whether implementations should return a complete message or a sentence fragment is not important, as long as they all agree. In this article's sample code, implementations return values as if they were prefixed with "The password?"
You apply the rule in the Test method, which iterates each character in the candidate password, calling Char.IsLetter to test them. The test method also counts the number of characters that return True for this condition.
Add Custom XML to App.config
You can save this XML on the file system, or in a database. But a more elegant solution is to add it to our application's configuration file. Do this by inserting this XML as a direct child of the document element, <configuration>:
Running your application at this point throws a ConfigurationErrorsException error: "Configuration system failed to initialize." The .NET runtime is confused because it doesn't recognize your "passwordPolicy" section. You defined this section previously, so you need to provide code that enables the runtime to understand it. This code is called a configuration section handler.
Configuration section handlers must be public, must have a public default constructor, and must implement an interface called IConfigurationSectionHandler. You need to provide only a single method to implement this interface: Create. Do so with this signature:
Function Create( _
ByVal parent As Object, _
ByVal configContext As Object, _
ByVal section As XmlNode) _
.NET calls this method on demand, the first time you try to access the data in that section. You need to worry about only one of the arguments this method accepts—section—which the runtime passes as a reference to the relevant XML node. The sample code's implementation simply iterates the context node's child elements, instantiates the proper rule for each one, and returns them as a List of IValidationRule implementations (see Listing 2).
So far, you have a custom configuration section and a handler that knows what to do with the section. Now you need to associate the two. You do this in the app.config file, as well. Create a <section> element as a child of a <configSections> element, which in turn is a child of the document element:
You need to specify three attributes in the <section> element. The name attribute specifies the name of the section—passwordPolicy, in this case. The allowExeDefinition attribute determines where the section is allowed to appear (see Table 1). Finally, type is a standard .NET-type specifier, and usually taking this form: Type name, Assembly name.
You access these rules programmatically using the ConfigurationManager class:
Dim obj As Object
Dim Rules As List(Of IValidationRule)
obj = ConfigurationManager.GetSection( _
Rules = CType(obj, List(Of IValidationRule))
You need to create a new configuration section, write an implementation of IConfigurationSectionHandler, and associate them in your config file. The .NET Framework will return an object you define, an object it knows nothing about at compile time. This is an elegant way to implement the configuration extensibility built into the .NET runtime system. It's a common extensibility design pattern, as well. Variations are used in the ASP.NET HTTP pipeline and extensively in ASP.NET 2.0's presentation classes.
Access Your IValidateRule Implementations
Finally, you can write code to access your various IValidationRule implementations. For convenience, you should make this code reusable, too. Simply create a PasswordPolicy class with a single shared method: Test (see Listing 3).
The code discussed so far assumes that the PasswordPolicy class lives in a standalone, Windows application. If you want to use it in more than one application, you need to cut-and-paste code and configuration sections—not the best approach. What you want is to employ binary reuse and make the class available to all applications on the computer it's installed on. You can do this by implementing only a handful of changes.
First, you must expose ConfigPasswordPolicySection, IValidationRule, and PasswordPolicy from a class library, which you install in the Global Assembly Cache.
Next, you define your config section in the .NET Framework's machine.config file (see the sidebar, "Find .NET's machine.config File"). You define the config section in the same place you define a private app.config: configuration/configSections. Note that your definition will have some company; this is where the .NET Framework defines its own configuration sections. The syntax in machine.config is also exactly the same as in app.config. Simply move the <section> node from app.config's <configSections> to machine.config's <configSections>.
So, assume you have four applications on a given computer, three that use the standard policies, and one that needs to make an exception. This is where configuration inheritance is useful; .NET applications can inherit machine-level configuration settings just as classes inherit base classes. If you define the passwordPolicy section in machine.config, all four applications inherit it. But the fourth—the exception—can define a passwordPolicy section in its app.config, which overrides the machine.config values.
That's about it. You can use your new component to enforce password-complexity rules as a matter of policy, without requiring code change. You can do this in a single application, globally on a computer, or adopt a best-of-both-worlds approach made possible by .NET configuration inheritance.
Best practices dictate that developing secure applications must be easy. The harder it is to be secure, the less secure your applications will be. A logical next step might be to write a control that descends from System.Windows.Forms.TextBox or System.Web.UI.WebControls.TextBox, and interacts with your new component to enforce these rules. Always make security as easy as possible.