To improve the situation, you have built into your system a password validity check, with a validity limit of 90 days. Three monts later, John’s password changed from "John" to "John1", another user with little more imagination changed from "ABCD" to "ABCDE".
The three functions included in this article will not make John’s life easier, but will improve the security of your system as a whole and let users know you really mean it.
g_repeat will not tolerate more than N repeated caracters in a password, such as "AAA" or "111" if N = 3.
* Program G_REPEAT.PRG * Author C. DESBOURSE * Description : * String analysis for repetitive characters * Calling Samples : =g_repeat(<expC1>,<expN1>) * Parameter List * expC1 : string to be analyzed * expN1 : maximum number of repetitions of the same * character allowed * Returns : * .T. if more repeat characters than maximum allowed FUNCTION g_repeat PARAMETERS cstring, nmax PRIVATE ln_total, lc_car, ln_loop, ll_reject, ln_occ ln_total = 0 ll_reject = .F. *-- Check parameters IF PARAMETERS()>=2 ; AND TYPE('CSTRING')='C' ; AND TYPE('NMAX')='N' ; AND nmax >=1 AND !EMPTY(cstring) FOR ln_loop =1 to LEN(ALLTRIM(cstring)) lc_car = SUBSTR(cstring,ln_loop,1) *-- character repeat detection ln_occ = OCCURS(lc_car,cstring) IF ln_occ > nmax ll_reject = .T. ENDIF ENDFOR ELSE *-- always reject if wrong or missing parameters ll_reject = .T. ENDIF RETURN ll_reject
* Program G_SEQUEN.PRG * Author C. DESBOURSE * Description * String analysis for consecutive characters * Calling Sample : =g_sequen(<expC1>,<expN1>) * Parameter List * expC1 : string to be analyzed * expN1 : maximum number of consecutive * characters allowed * Returns : * .T. if more consecutive characters * than maximum allowed FUNCTION g_sequen PARAMETERS cstring, nmax PRIVATE ln_total, lc_car, ln_loop, ll_reject, ; ln_oldval, ln_newval, ln_olddif, ln_newdif ln_total = 0 ll_reject = .F. IF PARAMETERS()>=2 ; AND TYPE('CSTRING')='C' ; AND TYPE('NMAX')='N' ; AND nmax >=1 AND !EMPTY(cstring) FOR ln_loop =1 to LEN(ALLTRIM(cstring)) lc_car = SUBSTR(cstring,ln_loop,1) IF ln_loop=1 lc_oldval=ASC(lc_car) ELSE lc_oldval=lc_newval ENDIF lc_newval=ASC(lc_car) IF ln_loop=1 ln_olddif = lc_newval - lc_oldval ELSE ln_olddif = ln_newdif ENDIF ln_newdif = Lc_newval - Lc_oldval IF ln_newdif=ln_olddif AND ln_loop > 1 ln_total=ln_total+1 IF ln_total >= nmax - 1 ll_reject = .t. ENDIF ELSE ln_total=0 ENDIF ENDFOR ELSE *-- always reject if wrong or missing parameters ll_reject = .T. ENDIF RETURN ll_reject
* Program : G_NOTSIM.PRG * Author : C. DESBOURSE * Description : String analysis for minimum difference * Calling Sample : =g_sequen(<expC1>,<expC2>,<expN1>) * Parameter List : expC1 : string to be analyzed * expC2 : second string * expN1 : minimum number of different characters required * Returns...........: .T. if less different characters than minimum required FUNCTION g_notsim PARAMETERS cstring1, cstring2, nmin PRIVATE ln_total, lc_car, ln_loop, ll_reject ln_total = 0 ll_reject = .T. *-- Check parameters IF PARAMETERS()>=3 AND TYPE('CSTRING1')='C' AND ; TYPE('CSTRING2')='C' AND TYPE('NMIN')='N' ; AND nmin >=1 AND !EMPTY(cstring1) AND!empty(cstring2) FOR ln_loop =1 to LEN(ALLTRIM(cstring1)) lc_car = SUBSTR(cstring2,ln_loop,1) *-- similarity detection IF !lc_car $ cstring1 *-- increment counter ln_total = ln_total +1 ENDIF IF ln_total >= nmin ll_reject = .F. EXIT ENDIF ENDFOR ELSE *-- always reject if wrong or missing parameters ll_reject = .T. ENDIF RETURN ll_reject
*-- Initialize ln_return = 0 ln_maxtry = 3 ln_pass = 1 *-- SECURITY HARD CODING *-- password minimum length ln_pwminln = 6 *-- max number of repeat characters in password ln_repeat = 3 *-- max number of characters in sequence (ABC, 123...) ln_sequenc = 3 *-- minimum number of different characters between old and new passwords ln_mindif = 2 *-- maximum password validity ( days ) ln_maxpwval = 90 *-- Setup default message lc_text = i("Enter new password, or <Esc> to Quit") lc_newpw1 = SPACE(ln_passwdlen) lc_newpw2 = SPACE(ln_passwdlen) *-- Entry loop DO WHILE .T. *-- modify message after 1st pass IF ln_pass > 1 lc_text = i("Enter new password again, or <Esc> to Quit") ENDIF *-- Display messsage DO g_dspst WITH lc_text *-- Edit loop DO WHILE .T. *-- Get password entry DO CASE *-- 1st pass CASE ln_pass = 1 *-- get password DO g_getpw WITH lc_newpw1 lc_newpw1 = ALLTRIM(lc_newpw1) *-- check password *-- minimum length IF LEN(ALLTRIM(lc_newpw1)) >= ln_pwminln *-- same characters repeated IF !g_repeat(lc_newpw1,ln_repeat) *-- sequences of characters IF !g_sequen(lc_newpw1,ln_sequenc) *-- skip in mode 2 ( employee maintenance - old pw not available ) IF ln_omode = 1 *-- not identical to old pw IF ! lc_newpw1 == lc_oldpw *-- minimum difference with old pw IF !g_notsim(lc_newpw1,lc_oldpw,ln_mindif) *-- new password accepted, continue with 2nd pass ln_pass = 2 ELSE *-- new password has less than ... # chars. vs old pw DO g_dspalt WITH i("The new password must have") + gc_eol + ; i(" a minimum of ") + STR(ln_mindif,2) + ; i(" characters") + gc_eol + ; i(" not included in the old password"), 2 ENDIF ELSE *-- new pw identical to old pw DO g_dspalt WITH ; i("The new password must not be identical") + gc_eol + ; i(" to the old password"), 2 ENDIF ELSE *-- new password accepted, continue with 2nd pass ln_pass = 2 ENDIF ELSE *-- sequences of chars. in pw DO g_dspalt WITH ; i("Password must not contain a sequence") + gc_eol + ; i(" of more than ") + STR(ln_sequenc,2) + ; i(" characters such as ABCD or 1234"), 2 ENDIF ELSE *-- repeat characters in pw DO g_dspalt WITH i("Password must not contain") + gc_eol + ; i("more than ") + STR(ln_repeat,2) + ; i(" times") + gc_eol + ; i("the same character"), 2 ENDIF ELSE *-- pw too short DO g_dspalt WITH i("Password must have a minimum of ") + ; STR(ln_pwminln,2) + i("characters"), 2 ENDIF *-- CASE ln_pass = 2 *-- get password again DO g_getpw WITH lc_newpw2 lc_newpw2 = ALLTRIM(lc_newpw2) IF lc_newpw1 == lc_newpw2 *-- both entries equal, accept password change ln_pass = 3 ELSE *-- second entry not correct IF !EMPTY(lc_newpw2) DO g_dspalt WITH i("The second password entry is not correct"), 2 ENDIF ENDIF ENDCASE lc_newpw2 = SPACE(ln_passwdlen) IF ln_pass >= 2 *-- Exit entry loop EXIT ENDIF lc_newpw1 = SPACE(ln_passwdlen) ENDDO *-- OK for password change, exit loop IF ln_pass = 3 ln_return = 1 EXIT ENDIF ENDDO && WHILE .t. *-- implement password change