What is the best way to check the strength of a password?

后端 未结 15 1180
孤独总比滥情好
孤独总比滥情好 2020-12-02 10:41

What is the best way of ensuring that a user supplied password is a strong password in a registration or change password form?

One idea I had (in python)

<         


        
相关标签:
15条回答
  • 2020-12-02 11:13

    I don't know if anyone will find this useful, but I really liked the idea of a ruleset as suggested by phear so I went and wrote a rule Python 2.6 class (although it's probably 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 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 unit tested

    0 讨论(0)
  • 2020-12-02 11:16

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

    -1 same as username
    +0 contains username
    +1 more than 7 chars
    +1 more than 11 chars
    +1 contains digits
    +1 mix of lower and uppercase
    +1 contains punctuation
    +1 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 not the most efficient, but seems reasonable. not sure FascistCheck => 'too similar to username' is worth it.

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

    maybe that should score lower.

    0 讨论(0)
  • 2020-12-02 11:17

    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 讨论(0)
  • 2020-12-02 11:19

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

    0 讨论(0)
  • 2020-12-02 11:19

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

    0 讨论(0)
  • 2020-12-02 11:21

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

    2: Don't force any rules on the user

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

    Instead check password strength

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

    I suggest as a minimum the following 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 sure enough its naive algorithm sees Password1! as good, whereas it is exceptionally weak. Make sure to disregard initial capital letters when scoring as well as trailing numbers 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 than forcing the user to bend to self-defeating rules, allow anything that will give a high enough score. How high depends on your use case.

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

    0 讨论(0)
提交回复
热议问题