Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Articles
Search: 

Three functions to improve application security
Christian Desbourse, January 1, 2001
You have set up an extensive security system to control access to your application through user log-in procedures, and password entry. But some people tend to be security reluctant, they write their password on the wall, or worse, everybody is using the same "Hello" password.
Summary
You have set up an extensive security system to control access to your application through user log-in procedures, and password entry. But some people tend to be security reluctant, they write their password on the wall, or worse, everybody is using the same "Hello" password.
Description
You have set up an extensive security system to control access to your application through user log-in procedures, and password entry. But some people tend to be security reluctant, they write their password on the wall, or worse, everybody is using the same "Hello" password.

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
g_sequen will not tolerate a sequence of more than N consecutive ASCII caracters, such as "ABCD", "1234" if N = 4
*  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
g_notsim will not let the user enter a new password with more than N caracters already contained in the old password.
*  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
Here is a sample illustrating the use of these three functions in a password check/entry procedure :
*-- 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
Strict security enforcement does not mean your application should not be user friendly. Note that every effort has been taken to let the user know why his password is rejected, or why is attempt to enter the application failed, including in the messages displayed all useful information for a graceful retry.
Christian Desbourse, Ir C Desbourse
Christian Desbourse is an independant consultant providing services in database development and Visual Foxpro programming. Independent developer of business applications. Business experience in logistic and industrial project management. Microsoft MVP from 1996 to 2002. MCP Visual Foxpro Desktop and distributed applications
More articles from this author
Christian Desbourse, January 1, 2001
Our sample data consists of two tables, an employee table and a table defining the timetable we want to display. The timetable is defined with the starting time (date, hour and minutes) and the employee in charge (hempl_id) of a number of time slot allocations of 30 minutes each. The data usually co...
Christian Desbourse, January 1, 2001
One of the most powerful command available with FoxPro’s language, SELECT-SQL allows you to extract from your databases the right data you need. For the beginner, FoxPro provides a built-in RBQE dialog which builds the SELECT-SQL statement and stores these in QPR files, which can be executed from w...
Christian Desbourse, January 1, 2001
If you include two tables in a query without specifying a join condition, every record in the first table will be joined with every record in the second table, with huge results as a consequence.
Christian Desbourse, January 1, 2001
Simulate SEEK, DO..WHILE and SCOPE Do you want to retrieve the five companies following ‘France restauration’ in Customers ? Oh I know, xBase junkies can do that with a SEEK, and a DO WHILE and ....but aren’t we talking SQL ? We will use a UDF to control the scope : FUNCTION incremnt PAR...