[ACCEPTED]-What is the best way to check the strength of a password?-passwords

Accepted answer
Score: 16

Depending on the language, I usually use 6 regular expressions to check if it has:

  • At least one uppercase and one lowercase letter
  • At least one number
  • At least one special character
  • A length of at least six characters

You 5 can require all of the above, or use a strength 4 meter type of script. For my strength meter, if 3 the password has the right length, it is 2 evaluated as follows:

  • One condition met: weak password
  • Two conditions met: medium password
  • All conditions met: strong password

You can adjust the 1 above to meet your needs.

Score: 10

The object-oriented approach would be a 4 set of rules. Assign a weight to each rule 3 and iterate through them. In psuedo-code:

abstract class Rule {

    float weight;

    float calculateScore( string password );

}

Calculating 2 the total score:

float getPasswordStrength( string password ) {     

    float totalWeight = 0.0f;
    float totalScore  = 0.0f;

    foreach ( rule in rules ) {

       totalWeight += weight;
       totalScore  += rule.calculateScore( password ) * rule.weight;

    }

    return (totalScore / totalWeight) / rules.count;

}

An example rule algorithm, based 1 on number of character classes present:

float calculateScore( string password ) {

    float score = 0.0f;

    // NUMBER_CLASS is a constant char array { '0', '1', '2', ... }
    if ( password.contains( NUMBER_CLASS ) )
        score += 1.0f;

    if ( password.contains( UPPERCASE_CLASS ) )
        score += 1.0f;

    if ( password.contains( LOWERCASE_CLASS ) )
        score += 1.0f;

    // Sub rule as private method
    if ( containsPunctuation( password ) )
        score += 1.0f;

    return score / 4.0f;

}
Score: 7

1: Eliminate often used passwords
Check the entered passwords against a list 24 of often used passwords (see e.g. the top 23 100.000 passwords in the leaked LinkedIn 22 password list: http://www.adeptus-mechanicus.com/codex/linkhap/combo_not.zip), make sure to include leetspeek substitutions: A@, E3, B8, S5, etc.
Remove 21 parts of the password that hit against this 20 list from the entered phrase, before going 19 to part 2 below.

2: Don't force any rules on the user

The golden rule of passwords 18 is that longer is better.
Forget about forced 17 use of caps, numbers, and symbols because 16 (the vast majority of) users will: - Make 15 the first letter a capital; - Put the number 14 1 at the end; - Put a ! after that if a symbol 13 is required.

Instead check password strength

For a decent starting point 12 see: http://www.passwordmeter.com/

I suggest as a minimum the following 11 rules:

Additions (better passwords)
-----------------------------
- Number of Characters              Flat       +(n*4)   
- Uppercase Letters                 Cond/Incr  +((len-n)*2)     
- Lowercase Letters                 Cond/Incr  +((len-n)*2)     
- Numbers                           Cond       +(n*4)   
- Symbols                           Flat       +(n*6)
- Middle Numbers or Symbols         Flat       +(n*2)   
- Shannon Entropy                   Complex    *EntropyScore

Deductions (worse passwords)
----------------------------- 
- Letters Only                      Flat       -n   
- Numbers Only                      Flat       -(n*16)  
- Repeat Chars (Case Insensitive)   Complex    -    
- Consecutive Uppercase Letters     Flat       -(n*2)   
- Consecutive Lowercase Letters     Flat       -(n*2)   
- Consecutive Numbers               Flat       -(n*2)   
- Sequential Letters (3+)           Flat       -(n*3)   
- Sequential Numbers (3+)           Flat       -(n*3)   
- Sequential Symbols (3+)           Flat       -(n*3)
- Repeated words                    Complex    -       
- Only 1st char is uppercase        Flat       -n
- Last (non symbol) char is number  Flat       -n
- Only last char is symbol          Flat       -n

Just following passwordmeter is not enough, because 10 sure enough its naive algorithm sees Password1! as 9 good, whereas it is exceptionally weak. Make 8 sure to disregard initial capital letters 7 when scoring as well as trailing numbers 6 and symbols (as per the last 3 rules).

Calculating Shannon entropy
See: Fastest way to compute entropy in Python

3: Don't allow any passwords that are too weak
Rather 5 than forcing the user to bend to self-defeating 4 rules, allow anything that will give a high 3 enough score. How high depends on your use 2 case.

And most importantly
When you accept the password and 1 store it in a database, make sure to salt and hash it!.

Score: 3

The two simplest metrics to check for are:

  1. Length. I'd say 8 characters as a minimum.
  2. Number of different character classes the password contains. These are usually, lowercase letters, uppercase letters, numbers and punctuation and other symbols. A strong password will contain characters from at least three of these classes; if you force a number or other non-alphabetic character you significantly reduce the effectiveness of dictionary attacks.

0

Score: 2

Cracklib is great, and in newer packages 14 there is a Python module available for it. However, on 13 systems that don't yet have it, such as 12 CentOS 5, I've written a ctypes wrapper 11 for the system cryptlib. This would also 10 work on a system that you can't install 9 python-libcrypt. It does require python with 8 ctypes available, so for CentOS 5 you have 7 to install and use the python26 package.

It 6 also has the advantage that it can take 5 the username and check for passwords that 4 contain it or are substantially similar, like 3 the libcrypt "FascistGecos" function 2 but without requiring the user to exist 1 in /etc/passwd.

My ctypescracklib library is available on github

Some example uses:

>>> FascistCheck('jafo1234', 'jafo')
'it is based on your username'
>>> FascistCheck('myofaj123', 'jafo')
'it is based on your username'
>>> FascistCheck('jxayfoxo', 'jafo')
'it is too similar to your username'
>>> FascistCheck('cretse')
'it is based on a dictionary word'
Score: 2

after reading the other helpful answers, this 10 is what i'm going with:

-1 same as username
+0 9 contains username
+1 more than 7 chars
+1 8 more than 11 chars
+1 contains digits
+1 7 mix of lower and uppercase
+1 contains punctuation
+1 6 non-printable char

pwscore.py:

import re
import string
max_score = 6
def score(username,passwd):
    if passwd == username:
        return -1
    if username in passwd:
        return 0
    score = 0
    if len(passwd) > 7:
        score+=1
    if len(passwd) > 11:
        score+=1
    if re.search('\d+',passwd):
        score+=1
    if re.search('[a-z]',passwd) and re.search('[A-Z]',passwd):
        score+=1
    if len([x for x in passwd if x in string.punctuation]) > 0:
        score+=1
    if len([x for x in passwd if x not in string.printable]) > 0:
        score+=1
    return score

example usage:

import pwscore
    score = pwscore(username,passwd)
    if score < 3:
        return "weak password (score=" 
             + str(score) + "/"
             + str(pwscore.max_score)
             + "), try again."

probably 5 not the most efficient, but seems reasonable. not 4 sure FascistCheck => 'too similar to username' is 3 worth it.

'abc123ABC!@£' = score 6/6 if 2 not a superset of username

maybe that should 1 score lower.

Score: 1

There's the open and free John the Ripper password cracker 2 which is a great way to check an existing 1 password database.

Score: 1

Well this is what I use:

   var getStrength = function (passwd) {
    intScore = 0;
    intScore = (intScore + passwd.length);
    if (passwd.match(/[a-z]/)) {
        intScore = (intScore + 1);
    }
    if (passwd.match(/[A-Z]/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/\d+/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/(\d.*\d)/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/[!,@#$%^&*?_~]/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/([!,@#$%^&*?_~].*[!,@#$%^&*?_~])/)) {
        intScore = (intScore + 5);
    }
    if (passwd.match(/[a-z]/) && passwd.match(/[A-Z]/)) {
        intScore = (intScore + 2);
    }
    if (passwd.match(/\d/) && passwd.match(/\D/)) {
        intScore = (intScore + 2);
    }
    if (passwd.match(/[a-z]/) && passwd.match(/[A-Z]/) && passwd.match(/\d/) && passwd.match(/[!,@#$%^&*?_~]/)) {
        intScore = (intScore + 2);
    }
    return intScore;
} 

0

Score: 0

In addition to the standard approach of 7 mixing alpha,numeric and symbols, I noticed 6 when I registered with MyOpenId last week, the 5 password checker tells you if your password 4 is based on a dictionary word, even if you 3 add numbers or replace alphas with similar 2 numbers (using zero instead of 'o', '1' instead 1 of 'i', etc.).

I was quite impressed.

Score: 0

I wrote a small Javascript application. Take 2 a look: Yet Another Password Meter. You can download the source and 1 use/modify it under GPL. Have fun!

Score: 0

I don't know if anyone will find this useful, but 6 I really liked the idea of a ruleset as 5 suggested by phear so I went and wrote a 4 rule Python 2.6 class (although it's probably 3 compatible with 2.5):

import re

class SecurityException(Exception):
    pass

class Rule:
    """Creates a rule to evaluate against a string.
    Rules can be regex patterns or a boolean returning function.
    Whether a rule is inclusive or exclusive is decided by the sign
    of the weight. Positive weights are inclusive, negative weights are
    exclusive. 


    Call score() to return either 0 or the weight if the rule 
    is fufilled. 

    Raises a SecurityException if a required rule is violated.
    """

    def __init__(self,rule,weight=1,required=False,name=u"The Unnamed Rule"):
        try:
            getattr(rule,"__call__")
        except AttributeError:
            self.rule = re.compile(rule) # If a regex, compile
        else:
            self.rule = rule  # Otherwise it's a function and it should be scored using it

        if weight == 0:
            return ValueError(u"Weights can not be 0")

        self.weight = weight
        self.required = required
        self.name = name

    def exclusive(self):
        return self.weight < 0
    def inclusive(self):
        return self.weight >= 0
    exclusive = property(exclusive)
    inclusive = property(inclusive)

    def _score_regex(self,password):
        match = self.rule.search(password)
        if match is None:
            if self.exclusive: # didn't match an exclusive rule
                return self.weight
            elif self.inclusive and self.required: # didn't match on a required inclusive rule
                raise SecurityException(u"Violation of Rule: %s by input \"%s\"" % (self.name.title(), password))
            elif self.inclusive and not self.required:
                return 0
        else:
            if self.inclusive:
                return self.weight
            elif self.exclusive and self.required:
                raise SecurityException(u"Violation of Rule: %s by input \"%s\"" % (self.name,password))
            elif self.exclusive and not self.required:
                return 0

        return 0

    def score(self,password):
        try:
            getattr(self.rule,"__call__")
        except AttributeError:
            return self._score_regex(password)
        else:
            return self.rule(password) * self.weight

    def __unicode__(self):
        return u"%s (%i)" % (self.name.title(), self.weight)

    def __str__(self):
        return self.__unicode__()

I hope someone finds 2 this useful!

Example Usage:

rules = [ Rule("^foobar",weight=20,required=True,name=u"The Fubared Rule"), ]
try:
    score = 0
    for rule in rules:
        score += rule.score()
except SecurityException e:
    print e 
else:
    print score

DISCLAIMER: Not 1 unit tested

More Related questions