Qual è il modo migliore per garantire che una password fornita dall'utente sia una password complessa in una registrazione o un modulo di cambio password?
Un'idea che ho avuto (in python)
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
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 правилам).
Расчет энтропии Шеннона
См.: Самый быстрый способ вычисления энтропии in Python
3: не допускайте слишком слабых паролей
Вместо того, чтобы заставлять пользователя подчиняться саморазрушительным правилам, разрешите все, что даст достаточно высокий балл. Как высоко зависит от вашего варианта использования.
È самое главное
Когда вы принимаете пароль и сохраняете его в базе данных, не забудьте засолить и хешировать его! .
A seconda della lingua, di solito uso espressioni regolari per verificare se ha:
Puoi richiedere tutto quanto sopra o utilizzare un tipo di script per il misuratore di forza. Per il mio misuratore di forza, se la password ha la lunghezza giusta, viene valutata come segue:
È possibile regolare quanto sopra per soddisfare le proprie esigenze.
L'approccio orientato agli oggetti sarebbe un insieme di regole. Assegna un peso a ciascuna regola e itera attraverso di esse. In psuedo-code:
abstract class Rule {
float weight;
float calculateScore( string password );
}
Calcolo del punteggio totale:
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;
}
Un algoritmo di regole di esempio, basato sul numero di classi di caratteri presenti:
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;
}
Le due metriche più semplici da verificare sono:
Cracklib è fantastico, e nei pacchetti più recenti c'è un modulo Python disponibile per questo. Tuttavia, su sistemi che ancora non ce l'hanno, come CentOS 5, ho scritto un wrapper ctypes per il cryptlib di sistema. Funzionerebbe anche su un sistema che non è possibile installare python-libcrypt. It does richiede python con ctypes disponibili, quindi per CentOS 5 devi installare e usare il pacchetto python26.
Ha anche il vantaggio di poter prendere il nome utente e controllare le password che lo contengono o sono sostanzialmente simili, come la funzione "FascistGecos" di libcrypt, ma senza richiedere all'utente di esistere in/etc/passwd.
La mia libreria ctypescracklib è disponibile su github
Alcuni esempi di utilizzo:
>>> 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'
dopo aver letto le altre risposte utili, questo è quello che sto andando:
-1 uguale al nome utente
+ 0 contiene nome utente
+ 1 più di 7 caratteri
+ 1 più di 11 caratteri
+ 1 contiene cifre
+ 1 mix di lettere maiuscole e minuscole
+ 1 contiene segni di punteggiatura
+ 1 carattere non stampabile
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
esempio di utilizzo:
import pwscore
score = pwscore(username,passwd)
if score < 3:
return "weak password (score="
+ str(score) + "/"
+ str(pwscore.max_score)
+ "), try again."
probabilmente non è il più efficiente, ma sembra ragionevole. non sono sicuro che FascistCheck => 'too similar to username' valga la pena.
'abc123ABC! @ £' = punteggio 6/6 se non è un superset di nome utente
forse dovrebbe segnare un punteggio più basso.
C'è l'open e free John the Ripper password cracker che è un ottimo modo per controllare un database di password esistente.
Bene, questo è quello che uso:
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;
}
Ho scritto una piccola applicazione Javascript. Date un'occhiata: Ancora un altro misuratore di password . Puoi scaricare il codice sorgente e usarlo/modificarlo in GPL. Divertiti!
Non so se qualcuno troverà questo utile, ma mi è davvero piaciuta l'idea di un set di regole come suggerito da phear, quindi sono andato a scrivere una regola della classe Python 2.6 (anche se probabilmente è compatibile con 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__()
Spero che qualcuno lo trovi utile!
Esempio di utilizzo:
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
DICHIARAZIONE DI NON RESPONSABILITÀ: nessuna unità testata
Oltre all'approccio standard di mixare alfa, numerico e simboli, ho notato che quando mi sono registrato con MyOpenId la scorsa settimana, il verificatore della password ti dice se la tua password è basata su un dizionario Word, anche se aggiungi numeri o sostituisci alpha con numeri simili (usando zero invece di "o", "1" invece di "i", ecc.).
Sono rimasto molto colpito.