Plateforme Level Extreme
Abonnement
Profil corporatif
Produits & Services
Support
Légal
English
Password Complexity Best Practise
Message
Information générale
Forum:
Visual FoxPro
Catégorie:
Codage, syntaxe et commandes
Versions des environnements
Visual FoxPro:
VFP 9 SP2
OS:
Windows Server 2012
Network:
Windows 2008 Server
Database:
MS SQL Server
Application:
Web
Divers
Thread ID:
01618616
Message ID:
01618648
Vues:
54
Hi Gerard,

First of all, I would not say this is best practice, it is just some code that might help you get started. Long time ago we used to FTP some files to the bank and we were supposed to change the FTP password regularly and the password needed to meet some conditions, so I created a couple of classes and a form to make the job of the users easier. The form will be to much work for me to put here, as it is based in our custom tables, but the "password generator" classes had all the code anyways and I put some example code of how to use it, hth
* Rules to get a password from this document: http://www.csb.gc.ca/eng/pdf/ftps.pdf
*
* Rules are:
*		The FTPS server performs the following validations on the password before accepting it:
*	1	· The password must be ASCII character encoded text.
*	2	· The password must be at least 12 characters long but no more than 40 characters long.
*	3	· The password must contain at least one lower-case alphabetic character, one uppercase
*		alphabetic character, and two numeric characters.
*	4	· The password must contain only a combination of the following characters: ‘a’ to ‘z’,
*		‘A’ to ‘Z’, and ‘0’ to ‘9’.
*	5	· The first eight characters of the password must contain at least one numeric character
*		and two alphabetic characters.
*	6	· The password cannot be a circular shift of the user id (note that such a password
*		would be invalid anyway because it would be only nine characters long and therefore
*		too short).
*	7	· The new password must differ from the previous password and cannot be a reverse or
*		circular shift of the previous password. For this comparison, uppercase letters and
*		lowercase letters are considered to be equal (Not implemented).
*	8	· The new password must have at least three characters that are different from the old
*		password. For this comparison, uppercase letters and lowercase letters are considered
*		to be equal.k

#define MAX_LENGTH			40				&& Rule 2
#define	MIN_LENGTH			12				&& Rule 2
#define MIN_LOWERCASE		1				&& Rule 3
#define MIN_UPPERCASE		1				&& Rule 3
#define MIN_DIGITS			2				&& Rule 3
#define ROOT_LENGTH			8				&& Rule 5
#define ROOT_ALPHACOUNT		2				&& Rule 5
#define ROOT_DIGITSCOUNT	2				&& Rule 5
#define ALL_DIGITS			'0123456789'
#define ALL_LETTERS			'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
#define MIN_DIFFCHARCOUNT	3

local lnLength

loRPG				= Createobject('RandomPasswordGenerator')
clear
*? 'Len' + Chr(9) + 'Digits' + Chr(9) + 'Lower' + Chr(9) + 'Upper' + Chr(9) + 'Root N' + Chr(9) + 'Root Char' + Chr(13)+Chr(10)

for i =1 to 25
	lcPassword			= loRPG.newPassword()
	lcError				= ''
	llPasswordOK		= loRPG.CheckPassword(lcPassword, @lcError)
	if llPasswordOK
		? lcPassword, 'OK'
	else
		Messagebox(lcError, 16, 'Invalid password: ' + lcPassword)
	endif
next i

lcError				= ''
lcPassword			= Inputbox('Enter the password')
llPasswordOK		= loRPG.CheckPassword(lcPassword, '', @lcError)
if llPasswordOK
	? lcPassword, 'OK'
else
	Messagebox(lcError, 16, 'Invalid password: ' + lcPassword)
endif


return



define class RandomPasswordGenerator as Session
	protected Random as Object
	
	function Init() as Boolean
		this.Random				= Newobject('RandomFunctions')
		return .t.
	endfunc
	
	procedure Destroy() as VOID
		this.Random				= null
		return null
	endproc
	
	function newPassword() as String
		local lnLength, lnLowerCaseCount, lnUpperCaseCount, lnDigitsCount, lnRootNumCount, lnRootCharCount
		local lcPassword, lcRoot
		
		lnLength			= this.Random.getRandomInteger(MIN_LENGTH, MAX_LENGTH)
		lnDigitsCount		= this.Random.getRandomInteger(MIN_DIGITS, lnLength - MIN_UPPERCASE - MIN_LOWERCASE)
		lnLowerCaseCount	= this.Random.getRandomInteger(MIN_LOWERCASE, lnLength - lnDigitsCount - MIN_UPPERCASE)
		lnUpperCaseCount	= lnLength - lnDigitsCount - lnLowerCaseCount
		lnRootNumCount		= this.Random.getRandomInteger(ROOT_DIGITSCOUNT, Min(lnDigitsCount, ROOT_LENGTH - ROOT_DIGITSCOUNT))
		lnRootCharCount		= ROOT_LENGTH - lnRootNumCount
		
		* Get the first 8 Characters, first the digits then the chars, later this will be shuffled
		lcUnshuffledRoot	= ''

		* Getting the digits
		for lnPosition = 1 to lnRootNumCount
			lcUnshuffledRoot	= lcUnshuffledRoot + this.Random.getRandomDigit()
		next lnPosition

		* Getting the chars, we need to keep a count on how many UpperCase and LowerCase chars are used
		for lnPosition = 1 to lnRootCharCount
			do case
				case lnUpperCaseCount > 0 and lnLowerCaseCount > 0
					lcChar				= this.Random.getRandomLetter()
				case lnUpperCaseCount > 0
					lcChar				= this.Random.getRandomUpperCase()
				case lnLowerCaseCount > 0
					lcChar				= this.Random.getRandomLowerCase()
				otherwise
					**** OOOOPSSS. Take a deep breath and think about this.
			endcase

			lcUnshuffledRoot	= lcUnshuffledRoot + lcChar
			if Isupper(lcChar)
				lnUpperCaseCount	= lnUpperCaseCount - 1
			else
				lnLowerCaseCount	= lnLowerCaseCount - 1
			endif
		next lnPosition

		lcRoot				= this.Random.StringShuffle(lcUnshuffledRoot)
		lnDigitsCount		= lnDigitsCount - lnRootNumCount
		lcPassword			= ''
		for lnPosition = 1 to lnDigitsCount
			lcPassword			= lcPassword + this.Random.getRandomDigit()
		next lnPosition

		for lnPosition = 1 to lnUpperCaseCount
			lcPassword			= lcPassword + this.Random.getRandomUpperCase()
		next lnPosition
		
		for lnPosition = 1 to lnLowerCaseCount
			lcPassword			= lcPassword + this.Random.getRandomLowerCase()
		next lnPosition

		return lcRoot + this.Random.StringShuffle(lcPassword)
	endfunc

	function CheckLength(tcPassword as String) as Boolean
		return Between(Len(tcPassword), MIN_LENGTH, MAX_LENGTH)
	endfunc

	function CheckValididyOfChars(tcPassword as String) as Boolean
		local lcInvalidCharacters

		lcInvalidCharacters	= Chrtran(Upper(tcPassword), ALL_DIGITS + ALL_LETTERS, '')
		return Len(lcInvalidCharacters) = 0
	endfunc
	
	function CheckEnoughStartingDigits(tcPasswordRoot as String) as Boolean
		local lnDigitsRoot

		lnDigitsRoot		= Len(tcPasswordRoot) - Len(Chrtran(tcPasswordRoot, ALL_DIGITS, ''))
		return lnDigitsRoot >= ROOT_DIGITSCOUNT
	endfunc

	function CheckEnoughStartingLetters(tcPasswordRoot as String) as Boolean
		local lnLettersRoot

		lnLettersRoot		= Len(tcPasswordRoot) - Len(Chrtran(tcPasswordRoot, ALL_DIGITS, ''))
		return lnLettersRoot >= ROOT_ALPHACOUNT
	endfunc
	
	function CheckEnoughDigits(tcPassword as String) as Boolean
		local lnDigits

		lnDigits			= Len(tcPassword) - Len(Chrtran(tcPassword, ALL_DIGITS, ''))
		return lnDigits >= MIN_DIGITS
	endfunc
	
	function CheckEnoughLowerCaseLetters(tcPassword as String) as Boolean
		local lnTotalLowerCase
		
		lnTotalLowerCase	= Len(tcPassword) - Len(Chrtran(tcPassword, Lower(ALL_LETTERS), ''))
		return lnTotalLowerCase >= MIN_LOWERCASE
	endfunc
	
	function CheckEnoughUpperCaseLetters(tcPassword as String) as Boolean
		local lnTotalUpperCase
		
		lnTotalUpperCase	= Len(tcPassword) - Len(Chrtran(tcPassword, ALL_LETTERS, ''))
		return lnTotalUpperCase >= MIN_UPPERCASE
	endfunc

	function CheckDifferentCharactersCount(tcCurrentPassword as String, tcPreviousPassword as String) as Integer
		return Len(Chrtran(Upper(tcCurrentPassword), Upper(tcPreviousPassword), ''))
	endfunc

	function CheckPassword(tcPassword as String, tcPreviousPassword, tcResult as String) as Boolean
		local lcRoot, lcResult, llSetResult, lcInvalidCharacters, lcPreviousPassword, lcNewCharacters

		llSetResult			= Pcount() = 3
		lcResult			= ''		
		lcPreviousPassword	= Iif(Vartype(tcPreviousPassword) = 'C', tcPreviousPassword, '')

		ASSERT .f.
				
		* Check the length
		if not Between(Len(tcPassword), MIN_LENGTH, MAX_LENGTH)
			lcResult			= 'Invalid Length. Current value: ' + Transform(Len(tcPassword))
		endif

		lcInvalidCharacters	= Chrtran(tcPassword, ALL_DIGITS + ALL_LETTERS + Lower(ALL_LETTERS), '')
		if Len(lcInvalidCharacters) # 0
			lcResult			= lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'The password has invalid characters: [' + lcInvalidCharacters + ']'
		endif

		lcRoot				= Left(tcPassword, ROOT_LENGTH)
		lnDigitsRoot		= Len(lcRoot) - Len(Chrtran(lcRoot, ALL_DIGITS, ''))
		lnUpperCRoot		= Len(lcRoot) - Len(Chrtran(lcRoot, ALL_LETTERS, '')) - lnDigitsRoot
		lnLowerCRoot		= Len(lcRoot) - lnDigitsRoot - lnUpperCRoot
		
		* At least two MIN_DIGITS digits at the beginning
		if lnDigitsRoot < MIN_DIGITS
			lcResult			= lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient digits at start: ' + Transform(lnDigitsRoot)
		endif
		
		* At least two ROOT_ALPHACOUNT chars within the firt ROOT_ALPHACOUNT characters
		if lnUpperCRoot + lnLowerCRoot < ROOT_ALPHACOUNT
			lcResult			= lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient letters at start: ' + Transform(lnUpperCRoot + lnLowerCRoot)
		endif

		* At Lest MIN_LOWERCASE lower case char
		lnTotalLowerCase	= Len(tcPassword) - Len(Chrtran(tcPassword, Lower(ALL_LETTERS), ''))
		if lnTotalLowerCase < MIN_LOWERCASE
			lcResult			= lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient lower case letters: ' + Transform(lnTotalLowerCase)
		endif

		* At Lest MIN_UPPERCASE lower case char
		lnTotalUpperCase	= Len(tcPassword) - Len(Chrtran(tcPassword, ALL_LETTERS, ''))
		if lnTotalUpperCase < MIN_UPPERCASE
			lcResult			= lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient upper case letters: ' + Transform(lnTotalUpperCase)
		endif

		* Check how many characters are different
		lcNewCharacters		= Chrtran(Upper(tcPassword), Upper(lcPreviousPassword), '')
		if Len(lcNewCharacters) < MIN_DIFFCHARCOUNT
			lcResult			= lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient new characters.' + Chr(13) + Chr(9) + 'Try using some from this group: [' + Chrtran(ALL_DIGITS + ALL_LETTERS + Lower(ALL_LETTERS), lcPreviousPassword, '') + ']'
		endif

		if llSetResult
			tcResult		= lcResult
		endif
		return Empty(lcResult)
	endfunc
enddefine

*******************************************************************************************************************
*                                                                                                                 *
* Collection of functions to return random numbers or letters                                                     *
*                                                                                                                 *
* Methods:                                                                                                        *
*                                                                                                                 *
* ReSeed               - Set a new seed for the internal random number generator                                  *
* getRandomDigit       - Returns a random digit as character ('0'..'9')                                           *
* getRandomLowerCase   - Returns a random lower case letter  ('a'..'z')                                           *
* getRandomUpperCase   - Returns a random upper case letter  ('A'..'Z')                                           *
* getRandomLetter      - Returns a random upper or lower case letter ('A'..'Z'|'a'..'z')                          *
*                                                                                                                 *
*                                                                                                                 *
*******************************************************************************************************************

define class RandomFunctions as Session
	function init() as Boolean
		Rand(-1)
		return .t.
	endfunc

	procedure ReSeed(tnNewSeed as Number) as VOID
		Rand(Iif(Vartype(tnNewSeed) = 'N', tnNewSeed, -1))
		return null
	endproc
	
	function getRandomDigit() as Character
		return this.getRandomAscii(Asc('0'), Asc('9'))
	endfunc

	function getRandomLowerCase() as Character
		return this.getRandomAscii(Asc('a'), Asc('z'))
	endfunc

	function getRandomUpperCase() as Character
		return this.getRandomAscii(Asc('A'), Asc('Z'))
	endfunc

	function getRandomLetter() as Character
		return Iif(Rand() < 0.5, this.getRandomUpperCase(), this.getRandomLowerCase())
	endfunc

	function getRandomNumber() as Number
		return Rand()
	endfunc

	function getRandomInteger(tnLowerLimit as Integer, tnUpperLimit as Integer) as Integer
		local lnLowerLimit, lnUpperLimit, lnAux
		
		lnLowerLimit			= Iif(Vartype(tnLowerLimit) # 'N' or Vartype(tnUpperLimit) # 'N', 0, Int(tnLowerLimit))
		lnUpperLimit			= Iif(Vartype(tnUpperLimit) # 'N', Iif(Vartype(tnLowerLimit) # 'N', 1, Int(tnLowerLimit)), Int(tnUpperLimit))
		if lnUpperLimit < lnLowerLimit
			lnAux					= lnLowerLimit
			lnLowerLimit			= lnUpperLimit
			lnUpperLimit			= lnAux
		endif
		
		* Default will be getRandomInteger(0, 1)
		* getRandom(5) = getRandomInteger(0, 5)
		
		return Int(Rand() * (lnUpperLimit - lnLowerLimit + 1)) + lnLowerLimit
	endfunc

	function getRandomAscii(tnLowerLimit as Integer, tnUpperLimit as Integer) as Character
		local lnLowerLimit, lnUpperLimit
		
		* Default will be getRandomAscii(0, 255)
		lnLowerLimit			= Iif(Vartype(tnLowerLimit) # 'N' or Vartype(tnUpperLimit) # 'N', 0, Int(tnLowerLimit))
		lnUpperLimit			= Iif(Vartype(tnUpperLimit) # 'N', Iif(Vartype(tnLowerLimit) # 'N', 255, Int(tnLowerLimit)), Int(tnUpperLimit))
		return Chr(this.getRandomInteger(lnLowerLimit, lnUpperLimit))
	endfunc

	* http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
	function StringShuffle(tcUnshuffled as String) as String
		local lnChars, lnChar, lcShuffled, lnPosition, lnRandomChar, lcTempChar
		
		lnChars				= Len(tcUnshuffled)
		lcShuffled			= tcUnshuffled

		for lnChar = lnChars to 1 step -1
			lnPosition			= this.getRandomInteger(1, lnChar)
			lcRandomChar		= Substr(lcShuffled, lnPosition, 1)
			lcTempChar			= Substr(lcShuffled, lnChar, 1)
			lcShuffled			= Stuff(lcShuffled, lnChar, 1, lcRandomChar)
			lcShuffled			= Stuff(lcShuffled, lnPosition, 1, lcTempChar)
		next lnChar
		
		return lcShuffled
	endfunc	
enddefine
"The five senses obstruct or deform the apprehension of reality."
Jorge L. Borges?

"Premature optimization is the root of all evil in programming."
Donald Knuth, repeating C. A. R. Hoare

"To die for a religion is easier than to live it absolutely"
Jorge L. Borges
Précédent
Répondre
Fil
Voir

Click here to load this message in the networking platform