How can I check the strength of a password (as a string
) using the .Net Framework?
问题:
回答1:
Basic but a logical one:
enum PasswordScore
{
Blank = 0,
VeryWeak = 1,
Weak = 2,
Medium = 3,
Strong = 4,
VeryStrong = 5
}
public class PasswordAdvisor
{
public static PasswordScore CheckStrength(string password)
{
int score = 1;
if (password.Length < 1)
return PasswordScore.Blank;
if (password.Length < 4)
return PasswordScore.VeryWeak;
if (password.Length >= 8)
score++;
if (password.Length >= 12)
score++;
if (Regex.Match(password, @"/\d+/", RegexOptions.ECMAScript))
score++;
if (Regex.Match(password, @"/[a-z]/", RegexOptions.ECMAScript) &&
Regex.Match(password, @"/[A-Z]/", RegexOptions.ECMAScript))
score++;
if (Regex.Match(password, @"/.[!,@,#,$,%,^,&,*,?,_,~,-,£,(,)]/", RegexOptions.ECMAScript))
score++;
return (PasswordScore)score;
}
}
Ref: http://passwordadvisor.com/CodeAspNet.aspx
回答2:
"Password strength" is a rather generic term, it could mean password character count, range of characters used (cardinality), time needed to crack (brute force) the password, etc..
One of the best ways to measure a password's cryptographic strength would be to calculate how many bits of entropy the password has (although, this is generally more accurate for measuring random passwords. You'll get an over estimated entropy result otherwise),
// Only accurate for passwords in ASCII.
public double CalculateEntropy(string password)
{
var cardinality = 0;
// Password contains lowercase letters.
if (password.Any(c => char.IsLower(c)))
{
cardinality = 26;
}
// Password contains uppercase letters.
if (password.Any(c => char.IsUpper(c)))
{
cardinality += 26;
}
// Password contains numbers.
if (password.Any(c => char.IsDigit(c)))
{
cardinality += 10;
}
// Password contains symbols.
if (password.IndexOfAny("\\|¬¦`!\"£$%^&*()_+-=[]{};:'@#~<>,./? ".ToCharArray()) >= 0)
{
cardinality += 36;
}
return Math.Log(cardinality, 2) * password.Length;
}
回答3:
If I may show my customized implementation of examples such as Teoman Soygul's (and others I've seen similar to his)... My implementation has a different scoring scheme and uses minimum requirements as well as a check for repeating characters.
public enum PasswordScore
{
Blank = 0,
TooShort = 1,
RequirementsNotMet = 2,
VeryWeak = 3,
Weak = 4,
Fair = 5,
Medium = 6,
Strong = 7,
VeryStrong = 8
}
public static PasswordScore CheckStrength(string password)
{
int score = 0;
// using three requirements here: min length and two types of characters (numbers and letters)
bool blnMinLengthRequirementMet = false;
bool blnRequirement1Met = false;
bool blnRequirement2Met = false;
// check for chars in password
if (password.Length < 1)
return PasswordScore.Blank;
// if less than 6 chars, return as too short, else, plus one
if (password.Length < 6)
{
return PasswordScore.TooShort;
}
else
{
score++;
blnMinLengthRequirementMet = true;
}
// if 8 or more chars, plus one
if (password.Length >= 8)
score++;
// if 10 or more chars, plus one
if (password.Length >= 10)
score++;
// if password has a number, plus one
if (Regex.IsMatch(password, @"[\d]", RegexOptions.ECMAScript))
{
score++;
blnRequirement1Met = true;
}
// if password has lower case letter, plus one
if (Regex.IsMatch(password, @"[a-z]", RegexOptions.ECMAScript))
{
score++;
blnRequirement2Met = true;
}
// if password has upper case letter, plus one
if (Regex.IsMatch(password, @"[A-Z]", RegexOptions.ECMAScript))
{
score++;
blnRequirement2Met = true;
}
// if password has a special character, plus one
if (Regex.IsMatch(password, @"[~`!@#$%\^\&\*\(\)\-_\+=\[\{\]\}\|\\;:'\""<\,>\.\?\/£]", RegexOptions.ECMAScript))
score++;
// if password is longer than 2 characters and has 3 repeating characters, minus one (to minimum of score of 3)
List<char> lstPass = password.ToList();
if (lstPass.Count >= 3)
{
for (int i = 2; i < lstPass.Count; i++)
{
char charCurrent = lstPass[i];
if (charCurrent == lstPass[i - 1] && charCurrent == lstPass[i - 2] && score >= 4)
{
score++;
}
}
}
if (!blnMinLengthRequirementMet || !blnRequirement1Met || !blnRequirement2Met)
{
return PasswordScore.RequirementsNotMet;
}
return (PasswordScore)score;
}
回答4:
Here's a simple one I've written:
/// <summary>
/// Evaluates a password
/// </summary>
public class PasswordEvaluator
{
public string Password { get; private set; }
public int Length { get; private set; }
public int TotalNumberChars { get; private set; }
public bool ContainsNumberChars{get { return TotalNumberChars > 0; }}
public int TotalUppercaseChars { get; private set; }
public bool ContainsUppercaseChars { get { return TotalUppercaseChars > 0; } }
public int TotalLowercaseChars { get; private set; }
public bool ContainsLowercaseChars { get { return TotalLowercaseChars > 0; } }
public int TotalSpecialChars { get; private set; }
public bool ContainsSpecialChars { get { return TotalSpecialChars > 0; } }
public PasswordEvaluator(string password)
{
Password = password.Trim();
Length = Password.Length;
foreach (var c in Password)
{
var charCode = (int)c;
if (charCode >= 48 && charCode <= 57) TotalNumberChars++;
else if (charCode >= 65 && charCode <= 90) TotalUppercaseChars++;
else if (charCode >= 97 && charCode <= 122) TotalLowercaseChars++;
else TotalSpecialChars++;
}
}
public bool StrongEnough()
{
// Minimum length requirement
if (Length < Settings.PasswordMinLength) return false;
// Mixed case requirement
if (!ContainsLowercaseChars && !ContainsUppercaseChars) return false;
// Special chars requirement
if (TotalSpecialChars < 3) return false;
// Min lower case chars requirement
if (TotalLowercaseChars < 3) return false;
// Min upper case chars requirement
if (TotalUppercaseChars < 3) return false;
return true;
}
}
You can define your own rules in StrongEnough()
回答5:
This is a simple JavaScript example of what I use, porting it to .Net shouldn't be very hard,
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;
}
回答6:
This is my own code used towards a password strength checker based on information entropy and NIST guidelines. However this method does not account for the 'human' language factor.
public enum PasswordScore
{
Blank,
VeryWeak,
Weak,
Medium,
Strong,
VeryStrong
}
public static PasswordScore CheckPasswordStrength(string password)
{
int N = 0;
int L = password.Length;
if (L == 0)
return PasswordScore.Blank;
if (Regex.IsMatch(password, @"[\d]", RegexOptions.ECMAScript))
N += 10;
if (Regex.IsMatch(password, @"[a-z]", RegexOptions.ECMAScript))
N += 26;
if (Regex.IsMatch(password, @"[A-Z]", RegexOptions.ECMAScript))
N += 26;
if (Regex.IsMatch(password, @"[~`!@#$%\^\&\*\(\)\-_\+=\[\{\]\}\|\\;:'\""<\,>\.\?\/£]", RegexOptions.ECMAScript) && password.Length > 8)
N += 33;
int H = Convert.ToInt32(L * (Math.Round(Math.Log(N) / Math.Log(2))));
if (H <= 32) return PasswordScore.VeryWeak;
if (H <= 48) return PasswordScore.Weak;
if (H <= 64) return PasswordScore.Medium;
if (H <= 80) return PasswordScore.Strong;
return PasswordScore.VeryStrong;
}
回答7:
Strength of a password should be checked on behalf of several parameters like the presence of special characters and numbers, length of the password etc.
I have found the below tutorial with nice demo:
http://tinytute.com/2014/06/03/animated-password-strength-checker-quick-easy/
The jQuery code block:
$(document).ready(function(){
$("#textBox").keyup(function(){
var passWord = $("#textBox").val();
var passLength = passWord.length;
var specialFlag = 0;
var numberFlag = 0;
var numberGenerator = 0;
var total = 0;
if(/^[a-zA-Z0-9- ]*$/.test(passWord) == false) {
specialFlag =20;
}
if(passWord.match(/[0-9]/)) {
numberFlag = 25;
}
if(passLength>4&&passLength<=6){
numberGenerator =25;
}else if(passLength>=7&&passLength<=9){
numberGenerator =35;
}else if(passLength>9){
numberGenerator =55;
}else if(passLength>0&&passLength<=4){
numberGenerator =15;
}else{
numberGenerator =0;
}
total = numberGenerator + specialFlag + numberFlag;
if(total<30){
$('#progressBar').css('background-color','#CCC');
}else if(total<60&&total>=30){
$('#progressBar').css('background-color','#FF6600');
}else if(total>=60&&total<90){
$('#progressBar').css('background-color','#FFCC00');
}else if(total>=90){
$('#progressBar').css('background-color','#0f0');
}
$('#progressBar').css('width',total+'%');
});
});