Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Better way to determine and police Password Strengths

, 13 Sep 2009
Rate this:
Please Sign up or sign in to vote.
Perhaps my Google search mo-jo has been acting up, but I could not find a good strong C# implementation for strong passwords (in fact I really couldn’t find much outside of logical cut & paste of implementations of random Information entropy implementations) . They were all predicated on t

Perhaps my Google search mo-jo has been acting up, but I could not find a good strong C# implementation for strong passwords (in fact I really couldn’t find much outside of logical cut & paste of implementations of random Information entropy implementations) . They were all predicated on the relatively standard assessment that all submitted passwords are random – uh huh!

For starters I recommend reading the article [http://en.wikipedia.org/wiki/Password_strength]. This is a good article covering the relative strengths of passwords, and gives a guide for determining the strength of a random password and a human derived password.

The major problem with passwords are that humans need to remember them, or they write them down. In an interesting technology twist historically you only used to have to worry about your co-workers having access/abusing your password because there was implicit physical security in place – you could only log on if you were physically in the office.  As such at that time your biggest threat was your co-workers, unfortunately the secondary defense of physical location has effectively been removed with the internet and VPN technology.  So now your threat count has increased from the people you work with to the entire world! Add to this these people are financially motivated and can directly target you – its a whole lot scarier out there now!

So before jumping into the implementations we need to go through well known things to avoid to help improve password strength:

  • Avoid sequences – keyboard or alphabet based (abcd, qwert, 1234, !@#$% etc)
  • Avoid dictionary words, especially common ones! Be aware that common misspellings are also used in dictionary based attacks – so unless your misspelling is VERY unusual then you can expect it to be in a dictionary!
  • Avoid leet/1337 password substitution of words (eg P@ssw0rd, M1cr0$0ft, 0\/\/n3d). Again these are now all in dictionaries, so while it may be harder to brute force – they are pretty trivial for a dictionary attack. Of course it doesnt hurt to be 1337, but it just really doesn’t help defend a targeted attack.
  • Avoid team names, socials, license names etc.

Things to avoid to minimize compromise exposure:

  • Use different passwords for different online accounts
  • Avoid using information about you that can be readily be found on the web as a password reset scheme. DOB, where you were born, school name etc.
  • If any account needs the most rigorous password control it is your email account. Nearly every online system ties back to an email account. If you need to reset a password, it normally goes to your email address. If that is compromised then that is really the opening of Pandora’s box.

Alright, lets start with the weakest ’safe’ approach – Information entropy:

  • This strength calculation only holds true for ‘random’ passwords. No human (at least that I know) can really generate a random password on their own. The best approach that I’m aware of is to start up notepad and get your two year old to start smacking your keyboard. Then take this text and randomly change case of characters and inserting special characters. Unfortunately this is still weak because we have 2 hands and the keyboard is naturally divided into where your hands go. This generation is not as randomly distributed as people would think – nor would I recommend it! But at least you have a starting point, but then you have to write it down!
  • [0-9] – 10 possible symbols per character – 3.32 bits of base2 log entropy
  • [a-z] – 26 possible symbols per character- 4.7 bits of base2 log entropy
  • [A-Z] – 26 possible symbols per character- 4.7 bits of base2 log entropy
  • [A-Z, 0-9] – 36 possible symbols per character- 5.17 bits of base2 log entropy
  • [A-Z,a-z] – 52 possible symbols per character- 5.7 bits of base2 log entropy
  • [A-Z, a-z, 0-9] – 62 possible symbols per character- 5.95 bits of base2 log entropy
  • [A-Z, a-z, 0-9, Special] – 94 possible symbols per character – 6.55 bits of base2 log entropy

So we can see that having a strong password using completely random information will be hard to generate on our own, yet this approach is what is what is most commonly used to in web applications to determine password strength. This is not strong enough because humans are naturally not random. Using this theory the following non-random passwords generate results that imply the passwords are strong:

  • 12345678901234567890 – 20*3.32 => 66.4 bits of entropy
  • !!!!!!!!!!!!!!!!!!!! – 10 * 6.55 => 65.5 bits of entropy
  • !@#$%^&*() – 10 & 6.55 => 65.5 bits of entropy
  • qwertyuiop[]qwertyuiop[] = 24 * 6.55 => 157.2 bits of entropy.

The more astute among us will see the last two passwords were generated by running your finger across the a keyboard line of on a US keyboard. To enter the 24 characters password took under 3 seconds. So if anyone saw someone entering a password like this at work or in a library – its pretty easy to duplicate. Plainly you can see that with human users they are going to opt for the easiest way to remember and enter a password – this will never be random!

So to help avoid our users from becoming victims we have to try to take away the ‘easy’ passage from them. We have to assume the password is not going to be mathematically random – so we need to start from a different position. We have to ensure we remove the human weaknesses that other ‘black hats’ are looking to exploit.

So going back to the beginning of the article we are going to create an interface to define a ‘password policy’ that provides us a way to help enforce a stronger passwords – or  at least allows systems to setup a common language for handling passwords.

   /// <span class="code-SummaryComment"><summary></span>
   /// Interface for defining a password policy
   /// <span class="code-SummaryComment"></summary></span>
   /// <span class="code-SummaryComment"><remarks></span>
   /// This security policy determines whether passwords
   /// meet pre-determined complexity requirements.
   ///
   /// If this policy is enabled, passwords must meet the
   /// following minimum requirements:
   ///
   /// Not contain the user's account name or parts of the
   /// user's full name that exceed four consecutive
   /// characters.
   /// Be at least <span class="code-SummaryComment"><see cref="MinimumPasswordLength"/></span>
   /// characters in length
   /// Contain characters from three of the following
   /// four categories:
   /// English uppercase characters (A through Z)
   /// English lowercase characters (a through z)
   /// Base 10 digits (0 through 9)
   /// Non-alphabetic characters (for example, !, $, #, %)
   ///
   /// Complexity requirements are enforced when passwords
   /// are changed or created.
   /// <span class="code-SummaryComment"></remarks></span>
   public interface IPasswordPolicy : IPolicy
   {
      /// <span class="code-SummaryComment"><summary></span>
      /// Indicates the minimum password strength index for
      /// this policy (see PasswordStrengthIndex)
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><remarks></span>
      /// This value is based of a calculation of
      /// information entropy after sequences
      /// and dictionary words have been
      /// removed.
      /// <span class="code-SummaryComment"></remarks></span>
      /// <span class="code-SummaryComment"><value></span>
      /// The minimum index of the password strength.
      /// <span class="code-SummaryComment"></value></span>
      PasswordStrengthIndex MinimumPasswordStrengthIndex
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// Gets or sets the minimum length of the password.
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><value>The minimum length of the password.</value></span>
      int MinimumPasswordLength
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// Gets or sets the maximum length of the password.
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><value>The maximum length of the password.</value></span>
      int MaximumPasswordLength
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// If policy requires mixed case
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><value>true if policy needs mixed case</value></span>
      bool RequireMixedCase
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// If policy needs digits
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><value>true if policy needs digits.</value></span>
      bool RequireDigits
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// If policy needs special characters
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><value></span>
      /// true if require special characters are needed
      /// <span class="code-SummaryComment"></value></span>
      bool RequireSpecialCharacters
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// Indicates if the username needs to be additionally
      /// supplied to verify the password complexity against
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><value></span>
      /// true require username to check password against
      /// <span class="code-SummaryComment"></value></span>
      bool RequireUsernameToCheckPasswordAgainst
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// Gets or sets the maximum count of characters
      /// in a sequence
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><value>The maximum count of characters</span>
      /// in a sequence.<span class="code-SummaryComment"></value></span>
      int MaximumCharacterSequenceCount
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// The duration of the lockout in minutes.
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><remarks></span>
      /// This security setting determines the number of
      /// minutes a locked-out account remains locked
      /// out before automatically becoming unlocked.
      /// The available range is from 0 minutes through
      /// 99,999 minutes.
      /// If you set the account lockout duration to less
      /// than zero, the account will be locked out until an
      /// administrator explicitly unlocks it. If an account
      /// lockout threshold is defined, the account lockout
      /// duration must be greater than or equal to
      /// the reset time.
      /// <span class="code-SummaryComment"></remarks></span>
      /// <span class="code-SummaryComment"><value>The duration of the lockout.</value></span>
      int LockoutDuration
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// Gets or sets the lockout threshold.
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><remarks></span>
      /// This security setting determines the number of
      /// failed logon attempts that causes a user
      /// account to be locked out. A locked-out
      /// account cannot be used until it is reset
      /// by an administrator or until the
      /// lockout duration for the account has expired. You
      /// can set a value between 0 and 999 failed
      /// logon attempts. If you set the value to 0,
      /// the account will never be locked out.
      /// <span class="code-SummaryComment"></remarks></span>
      /// <span class="code-SummaryComment"><value>The lockout threshold.</value></span>
      int LockoutThreshold
      {
         get;
         set;
      }

      /// <span class="code-SummaryComment"><summary></span>
      /// Reset account lockout after X minutes
      /// <span class="code-SummaryComment"></summary></span>
      /// <span class="code-SummaryComment"><remarks></span>
      /// This security setting determines the number of
      /// minutes that must elapse after a failed logon
      /// attempt before the failed logon attempt
      /// counter is reset to 0 bad logon attempts.
      /// The available range is 1 minute to
      /// 99,999 minutes.
      /// <span class="code-SummaryComment"></remarks></span>
      /// <span class="code-SummaryComment"><value>The duration of the lockout.</value></span>
      int LockoutResetInMinutes
      {
         get;
         set;
      }
   }

You can see this password policy template extends the initial outline to not only provide guidance for the number of entropy bits, but allows for the policy to cover the lock out strategy in the case of incorrect password handling and password expiry approaches. If you look at the source code you will also see the options that are available, but for the sake of this article we are trying to keep on point :-)

So on to the actual strength testing, this oddly is rather simple at the end of the day. We are going to use an Interface definition (IPassword) for the Password processor (makes testing & mocking easier) so we can actually have multiple implementations (think MEF!).  Now the actual implementation.

  1. Check for sequences using various lookup tables to determine if any sequences exist. If a sequence length is detected, and is longer than allowed the password fails the policy. The tables include:
    • Alphabetic + numeric sequence
    • QWERTY US Keyboard
    • QWERTY UK Keyboard
    • AZERTY Keyboard
  2. Perform simple DecodeEliteEncoding then perform a simple hardcoded dictionary match of well known super common passwords
  3. If supplied (and if required) compare password elements to the user name

The end implementation is still fairly simple and it would be fairly easy to improve on this implementation.  The most obvious ones are to support a custom dictionary and add more custom keyboard sequences. Other extensions would be to store the passwords and become a real password token service. We can leave it up to the reader to provide an implementation of IPassword to call the Google password rating service rather than the above implementation:

https://www.google.com/accounts/RatePassword?Passwd=csharphacker

All good stuff! I hope this helps (and the source code) people provide a better approach to helping strengthen passwords.

[Download source code here]

The linked source code is liable to change over time so check back often. The source code uses the Microsoft testing framework and currently has 100% code coverage! Although I don’t think 100% is all that people think it is.

Finally the goal is to make everything a more secure place – and in reality the best approach is to use a strong memorable password in conjunction with a hardware token that changes every minute.

As always feedback is welcome!

Gareth

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

GarethI

United States United States
I'm Gareth and am a guy who loves software! My day job is working for a retail company and am involved in a large scale C# project that process large amounts of data into up stream data repositories.
 
My work rule of thumb is that everyone spends much more time working than not, so you better enjoy what you do!
 
Needless to say - I'm having a blast.
 
Have fun,
 
Gareth

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 13 Sep 2009
Article Copyright 2009 by GarethI
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid