Как лучше проверить надежность пароля? - PullRequest
43 голосов
/ 16 сентября 2008

Как лучше всего убедиться, что введенный пользователем пароль является надежным паролем в форме регистрации или смены пароля?

Одна идея у меня была (на питоне)

def validate_password(passwd):
    conditions_met = 0
    conditions_total = 3
    if len(passwd) >= 6: 
        if passwd.lower() != passwd: conditions_met += 1
        if len([x for x in passwd if x.isdigit()]) > 0: conditions_met += 1
        if len([x for x in passwd if not x.isalnum()]) > 0: conditions_met += 1
    result = False
    print conditions_met
    if conditions_met >= 2: result = True
    return result

Ответы [ 15 ]

17 голосов
/ 16 сентября 2008

В зависимости от языка, я обычно использую регулярные выражения, чтобы проверить, имеет ли он:

  • Как минимум одна заглавная буква и одна строчная буква
  • хотя бы одно число
  • хотя бы один специальный символ
  • Длина не менее шести символов

Вы можете потребовать все вышеперечисленное или использовать сценарий типа измерителя прочности. Для моего измерителя прочности, если пароль имеет правильную длину, он оценивается следующим образом:

  • Одно условие выполнено: слабый пароль
  • Выполнены два условия: средний пароль
  • Все условия выполнены: надежный пароль

Вы можете настроить вышеперечисленное в соответствии со своими потребностями.

9 голосов
/ 16 сентября 2008

Объектно-ориентированный подход был бы набором правил. Присвойте вес каждому правилу и повторите их. В псевдо-код:

abstract class Rule {

    float weight;

    float calculateScore( string password );

}

Расчет общего балла:

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;

}

Пример алгоритма правила, основанный на количестве имеющихся классов символов:

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;

}
4 голосов
/ 23 мая 2018

1: исключить часто используемые пароли
Проверьте введенные пароли по списку часто используемых паролей (см., Например, верхние 100 000 паролей в утечке списка паролей LinkedIn: http://www.adeptus -mechanicus.com / codex / linkhap / combo_not.zip ), убедитесь, что включить замены leetspeek : A @, E3, B8, S5 и т. Д.
Удалите части пароля, которые попали в этот список, из введенной фразы, прежде чем перейти к части 2 ниже.

2: не навязывать пользователю никаких правил

Золотое правило паролей гласит: чем дольше, тем лучше.
Забудьте о принудительном использовании заглавных букв, цифр и символов, потому что (подавляющее большинство) пользователей будут: - сделать первую букву заглавной; - поставить номер 1 в конце; - Поставьте ! после этого, если требуется символ.

Вместо этого проверьте надежность пароля

Достойную отправную точку см .: http://www.passwordmeter.com/

Я предлагаю как минимум следующие правила:

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

Простого следования passwordmeter недостаточно, потому что достаточно наивный алгоритм считает Password1! хорошим, тогда как он исключительно слабый. Обязательно игнорируйте начальные заглавные буквы при подсчете очков, а также конечные цифры и символы (согласно последним 3 правилам).

Расчет энтропии Шеннона
Смотрите: Самый быстрый способ вычисления энтропии в Python

3: Не допускайте слишком слабых паролей
Вместо того, чтобы заставлять пользователя подчиняться саморазрушительным правилам, разрешите все, что даст достаточно высокий балл. Как высоко зависит от вашего варианта использования.

И самое главное
Когда вы принимаете пароль и сохраняете его в базе данных, не забудьте засолить и хешировать его! .

3 голосов
/ 16 сентября 2008

Две самые простые метрики для проверки:

  1. Длина. Я бы сказал, 8 символов как минимум.
  2. Количество различных классов символов, которые содержит пароль. Обычно это строчные буквы, прописные буквы, цифры и знаки препинания и другие символы. Надежный пароль будет содержать символы как минимум из трех из этих классов; если вы вводите число или другой не алфавитный символ, вы значительно снижаете эффективность атак по словарю.
2 голосов
/ 28 ноября 2010

после прочтения других полезных ответов вот что я собираюсь сделать:

-1 так же, как имя пользователя
+0 содержит имя пользователя
+1 более 7 символов
+1 более 11 символов
+1 содержит цифры
+1 смесь строчных и прописных букв
+1 содержит пунктуацию
+1 непечатаемый символ

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

пример использования:

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

вероятно, не самый эффективный, но кажется разумным. не уверен, что FascistCheck => 'слишком похоже на имя пользователя' стоит того.

'abc123ABC! @ £' = оценка 6/6, если не расширенная часть имени пользователя

может быть, это должно быть ниже.

2 голосов
/ 16 ноября 2010

Cracklib великолепен, и в новых пакетах для него доступен модуль Python. Однако в системах, в которых его еще нет, например, в CentOS 5, я написал оболочку ctypes для системной библиотеки cryptlib. Это также будет работать в системе, в которой вы не можете установить python-libcrypt. Для требуется python с доступными ctypes, поэтому для CentOS 5 вам необходимо установить и использовать пакет python26.

Он также обладает тем преимуществом, что может принимать имя пользователя и проверять пароли, которые его содержат или в значительной степени схожи, например, функция libcrypt "FascistGecos", но не требует наличия пользователя в /etc/passwd.

Моя библиотека ctypescracklib доступна на github

В некоторых примерах используется:

>>> 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'
1 голос
/ 02 сентября 2011

Ну, это то, что я использую:

   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;
} 
1 голос
/ 16 сентября 2008

Существует открытый и бесплатный взломщик паролей John the Ripper , который является отличным способом проверить существующую базу паролей.

0 голосов
/ 05 января 2019

Каков наилучший способ убедиться, что введенный пользователем пароль является надежным паролем в форме регистрации или смены пароля?

Не оценивайте сложность и / или силу, пользователи найдут способ обмануть вашу систему или настолько разочароваться, что уйдут. Это только дает вам ситуации , как это . Просто требуется определенная длина и чтобы утечка паролей не использовалась. Бонусные баллы: убедитесь, что все, что вы внедряете, позволяет использовать менеджеры паролей и / или 2FA.

0 голосов
/ 09 декабря 2009

Я не знаю, найдет ли кто-нибудь это полезным, но мне действительно понравилась идея набора правил, предложенного phear, поэтому я пошел и написал класс Python 2.6 для правил (хотя он, вероятно, совместим с 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__()

Я надеюсь, что кто-то найдет это полезным!

Пример использования:

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Не проверено на единицу изделия

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...