Password Complexity

Before I re-invent the wheel has anyone done a password complexity function/method, or algorithm I could use. Just adding the finishing touches to a web app and would like something to test a password and return true or false as to whether it meets defined criteria.

i.e. is it greater than 8 characters, does it contain special characters, at least one number, a capitalised letter etc.

Any pointers as always appreciated.

I think I have one around here somewhere… I just have to recall which project its in.

It assesses the password strength based on an algorithm I found on the net years ago

Public Function Password_Strength(pwd As String) as Integer

Dim i As Integer
Dim j As Integer
Dim c As String
Dim cnt_upper As Integer
Dim cnt_lower As Integer
Dim cnt_numeric As Integer
Dim cnt_middle As Integer
Dim cnt_con_upper As Integer ' consecutive Uppercase letters
Dim cnt_con_lower As Integer
Dim cnt_con_numeric As Integer
Dim cnt_symbols As Integer
Dim len_pwd As Integer
Dim last_c As String
Dim score As Integer
Dim min_req As Integer
Dim repeat As Integer
Dim sFWD As String
Dim sREV As String
Dim total_score As Integer
len_pwd=Len(pwd)
cnt_upper=0
cnt_con_upper=0
cnt_lower=0
cnt_con_lower=0
cnt_numeric=0
cnt_con_numeric=0
cnt_symbols=0
cnt_middle=0
If len_pwd>0 Then
      For i=1 To len_pwd
            c=Mid(pwd,i,1)
            If InStrB(const_UpperCase,c)>0 Then
                  cnt_upper=cnt_upper+1
                  If i>1 And InStrB(const_UpperCase,last_c)>0 Then cnt_con_upper=cnt_con_upper+1
            End If
            If InStrB(const_LowerCase,c)>0 Then
                  cnt_lower=cnt_lower+1
                  If i>1 And InStrB(const_LowerCase,last_c)>0 Then cnt_con_lower=cnt_con_lower+1
            End If
            If InStrB(const_number,c)>0 Then
                  cnt_numeric=cnt_numeric+1
                  If i>1 Then
                        If i<Len(pwd) Then cnt_middle=cnt_middle+1
                        If InStrB(const_number,last_c)>0 Then cnt_con_numeric=cnt_con_numeric+1
                  End If
            End If
            If InStrB(const_SYMBOLS,c)>0 Then
                  cnt_symbols=cnt_symbols+1
                  If i>1 And i<Len(pwd) Then cnt_middle=cnt_middle+1
            End If
            last_c=c
      Next i
Else
      Return -1
End If

//
// Additions
//
'
' Number of characters [Fail<min Warn=n/a Good=min Great>min]
'
score=(len_pwd*4)
total_score=total_score+score
'
' Uppercase Characters [Fail=0 Good=1 Great>1]
'
score=((len_pwd-cnt_upper)*2)
If cnt_upper=len_pwd Or cnt_upper=0 Then score=0
total_score=total_score+score
'
' Lowercase Characters [Fail=0 Good=1 Great>1]
'
score=((len_pwd-cnt_lower)*2)
If cnt_lower=len_pwd Or cnt_lower=0 Then score=0
total_score=total_score+score
'
' Numbers [Fail=0 Good=1 Great>1]
'
score=cnt_numeric*4
If cnt_numeric=len_pwd Or (cnt_upper+cnt_lower=0) Then score=0
total_score=total_score+score
'
' Symbols [Fail=0 Good=1 Great>1]
'
score=cnt_symbols*6
total_score=total_score+score
'
' Numbers and/or Symbols in the Middle
'
score=cnt_middle*2
total_score=total_score+score
'
' Minimum Requirements
'
min_req=0
If cnt_upper>0 Then min_req=min_req+1
If cnt_lower>0 Then min_req=min_req+1
If cnt_numeric>0 Then min_req=min_req+1
If cnt_symbols>0 Then min_req=min_req+1

score=0
If len_pwd>=min_pwd_length Then
      min_req=min_req+1
      min_req=Max(0,min_req)
      score=min_req*2
End If
total_score=total_score+score
'
//
// Deductions
//
'
' Letters Only
'
score=0
If cnt_upper+cnt_lower=len_pwd Then score=len_pwd
total_score=total_score-score
'
' Numbers Only
'
score=0
If cnt_numeric=len_pwd Then score=len_pwd
total_score=total_score-score
'
' Repeat Characters [insensitive]
'
score=0
For i=1 To len_pwd-1
      repeat=0
      For j=i+1 To len_pwd
            If Mid(pwd,i,1)=Mid(pwd,j,1) Then
                  repeat=repeat+2
            End If
      Next j
      If repeat>0 Then score=score+(repeat*(repeat-1))
Next i
total_score=total_score-score
'
' Consecutive UpperCase Letters
'
score=0
If cnt_con_upper>0 Then score=cnt_con_upper*2
total_score=total_score-score
'
' Consecutive Lowercase Letters
'
score=0
If cnt_con_lower>0 Then score=cnt_con_lower*2
total_score=total_score-score
'
' Consecutive Numbers
'
score=0
If cnt_con_numeric>0 Then score=cnt_con_numeric*2
total_score=total_score-score
'
' Sequential Letters (3 or more)
'
repeat=0
For i=1 To Len(const_UpperCase)-3
      sFWD=Mid(const_UpperCase,i,3)
      sREV=reverse(sFWD)
      If InStr(pwd,sfwd)>0 Or InStr(pwd,srev)>0 Then repeat=repeat+1
Next i
score=0
If repeat>0 Then score=repeat*3
total_score=total_score-score
'
' Sequential Numbers (3 or more)
'
repeat=0
For i=1 To Len(const_number)-3
      sFWD=Mid(const_number,i,3)
      sREV=reverse(sFWD)
      If InStr(pwd,sfwd)>0 Or InStr(pwd,srev)>0 Then repeat=repeat+1
Next i
score=0
If repeat>0 Then score=repeat*3
total_score=total_score-score
'
'
'
total_SCORE=Max(0,Min(100,total_score))
Return total_SCORE
end function

Total Score is

 -1 "No Password"
 0 To 19 "Very Weak"
20 To 39  "Weak"
40 To 59  "Good"
60 To 79   "Strong"
80 To 100 "Very Strong"

@Kem Tekinay has AnalyzePasswordStrength in M_String module …

M_String

Thanks both, thats saved me an afternoon :slight_smile:

I would recommend NOT including a strength indicator, since it really is meaningless. Common passwords will pass the test despite being weak because they are common, reused passwords cannot be tested at all, and purple monkey dishwasher is a stronger password than 1Nsecure! but strength detection would tell you the opposite. Though both are on the weaker side due to being entirely dictionary words.

These things give users a false sense of security.

The only good password is a long, random, and unique password. While that’s not your crusade to fight, you might consider not giving positive reinforcement to poor password practices.

[quote=403696:@Thom McGrath]I would recommend NOT including a strength indicator, since it really is meaningless. Common passwords will pass the test despite being weak because they are common, reused passwords cannot be tested at all, and purple monkey dishwasher is a stronger password than 1Nsecure! but strength detection would tell you the opposite. Though both are on the weaker side due to being entirely dictionary words.

These things give users a false sense of security.

The only good password is a long, random, and unique password. While that’s not your crusade to fight, you might consider not giving positive reinforcement to poor password practices.[/quote]

I wrote (for a customer) a Xojo class that checks to see if a password has been compromised before using the "have i been pwned” API. Now this doesnt tell you if a password is strong or not, it can tell you if it is one of the ones that the hackers have out there (been pwned).

I wrote an artice about it for XDev…

And I agree with Thom that a longer password is better than one that just has upper/lower alpha & numbers & special characters.

LIked the article Scott. The method Dave provided will be fine for my purposes, its purely to ensure people don’t even start with an easy password. I like phrases, easier to remember, long and complex, but most users prefer something short and snappy - which hence, compromises their security and they need to be ‘warned off’.

For Mac OS you also might want to check out SFPasswordAssistantMBS