Skip to content

Instantly share code, notes, and snippets.

@ivanionut
Created July 7, 2016 15:56
Show Gist options
  • Save ivanionut/eece96bc845a1b62f8ac894c68cc5a9b to your computer and use it in GitHub Desktop.
Save ivanionut/eece96bc845a1b62f8ac894c68cc5a9b to your computer and use it in GitHub Desktop.
A ColdFusion component for testing the strength of a password, which is necessary for enhancing the security of an application.
<!------------------------------------------------------------------------------
|| Component : passwordCheck.cfc
|| Author : Jason Luttrell
|| Description : Functionality to test for password strength.
|| Public Methods : init()
|| : initialize component
|| isPasswordValid()
|| : returns boolean indicating whether or not the
|| password meets the minimum requirements.
|| getErrors()
|| : returns an HTML list of the reasons a password
|| does not meet the minimum requirements.
|| getStrength()
|| : returns the strength of the password (i.e., WEAK,
|| MEDIUM, or STRONG).
|| Notes : Use of this component does NOT satisfy all security
|| requirements. There are three more additional requirements
|| that must be implemented on every application:
|| (1) Passwords should be set to expire every 90 days and
|| the application should remember the last five used
|| passwords.
|| (2) The passwords must be encrypted when they are stored
|| in the database.
|| (3) When validating a user, password matching must use
|| CASE-SENSITIVE comparisons. By default, ColdFusion
|| and MS SQL Server use case-INsensitive comparisons.
------------------------------------------------------------------------------->
<!---
==========================================
|| SAMPLE CODE TO INVOKE THIS COMPONENT ||
==========================================
<cfscript>
// Initialize component
PasswordCheckObj = createobject("component","#cfcRoot#passwordCheck");
PasswordCheckObj.init(
form.password,
form.userName,
form.firstName,
form.lastName,
form.emailAddress,
isSuperAdmin
);
</cfscript>
<cfif not PasswordCheckObj.isPasswordValid()>
The password is considered #PasswordCheckObj.getStrength()# for the
following reasons:
<blockquote>
#PasswordCheckObj.getErrors()#
</blockquote>
</cfif>
=====================
|| END SAMPLE CODE ||
=====================
--->
<cfcomponent output="no">
<cfscript>
//========================================================================//
// INITIALIZE COMPONENT VARIABLES //
//========================================================================//
variables.instance = structNew();
// Define minimum password requirements using constants
instance.MIN_PASSWORD_LENGTH = 8;
instance.MIN_DIGITS = 1;
instance.MIN_LOWER_CASE_LETTERS = 1;
instance.MIN_SPECIAL_CHARACTERS = 1;
instance.MIN_UPPER_CASE_LETTERS = 1;
instance.MIN_NON_LETTER_CHARACTERS = 2;
// Define what a "strong" password is for root/super admins, using constants
instance.OPTIMAL_PASSWORD_LENGTH = 10;
instance.OPTIMAL_DIGITS = 1;
instance.OPTIMAL_LOWER_CASE_LETTERS = 2;
instance.OPTIMAL_SPECIAL_CHARACTERS = 1;
instance.OPTIMAL_UPPER_CASE_LETTERS = 2;
instance.OPTIMAL_NON_LETTER_CHARACTERS = 3;
// Internal variables
instance.countDigits = 0;
instance.countLowerCaseLetters = 0;
instance.countSpecialCharacters = 0;
instance.countUpperCaseLetters = 0;
instance.emailAddress = "";
instance.errorMessages = "";
instance.firstName = "";
instance.isPasswordValid = false;
instance.lastName = "";
instance.password = "";
instance.passwordLength = 0;
instance.passwordStrength = "";
instance.requireOptimalPassword = false;
instance.userName = "";
//========================================================================//
// *** PUBLIC METHODS *** //
//========================================================================//
//--------------------------------------------------------------------------
// Function : init()
// Type : public
// Arguments : (1) password (string) - the password to test
// (2) userName (string) - ensures that the password is not
// the same as the person's user name.
// (3) firstName (string) - ensures that the password is not
// the same as the person's first name.
// (4) lastName (string) - ensures that the password is not
// the same as the person's first name.
// (5) emailAddress (string) - ensures that the password is
// not the same as the person's e-mail address.
// (6) requireOptimalPassword (boolean) [OPTIONAL] - test the
// password for the best possible entropy (for root/super
// admin accounts).
// Actions : The primary function for instantiating this component.
// Returns : this; the variable scope for this instance
//--------------------------------------------------------------------------
function init(password, userName, firstName, lastName, emailAddress)
{
// Define local scope
var local = structNew();
// Get function arguments
local.password = trim(arguments.password);
local.userName = trim(arguments.userName);
local.firstName = trim(arguments.firstName);
local.lastName = trim(arguments.lastName);
local.emailAddress = trim(arguments.emailAddress);
if (arrayLen(arguments) gte 6)
local.requireOptimalPassword = arguments[6];
else
local.requireOptimalPassword = false;
// Get password length
local.passwordLength = len(local.password);
// Count number of lower-case letters
local.countLowerCaseLetters = 0;
for (i=1; i lte local.passwordLength; i=i+1) {
local.letterIndex = reFind("[a-z]", local.password, i);
if (local.letterIndex gt 0) {
i = local.letterIndex;
local.countLowerCaseLetters = local.countLowerCaseLetters + 1;
}
}
// Count number of upper-case letters
local.countUpperCaseLetters = 0;
for (i=1; i lte local.passwordLength; i=i+1) {
local.letterIndex = reFind("[A-Z]", local.password, i);
if (local.letterIndex gt 0) {
i = local.letterIndex;
local.countUpperCaseLetters = local.countUpperCaseLetters + 1;
}
}
// Count number of digits
local.countDigits = 0;
for (i=1; i lte local.passwordLength; i=i+1) {
local.numberIndex = reFind("[0-9]", local.password, i);
if (local.numberIndex gt 0) {
i = local.numberIndex;
local.countDigits = local.countDigits + 1;
}
}
// Count number of special chars
local.countSpecialCharacters = 0;
for (i=1; i lte local.passwordLength; i=i+1) {
local.specialCharIndex = reFind("[\W]", local.password, i);
if (local.specialCharIndex gt 0) {
i = local.specialCharIndex;
local.countSpecialCharacters = local.countSpecialCharacters + 1;
}
}
// Save variables to component scope
instance.countDigits = local.countDigits;
instance.countLowerCaseLetters = local.countLowerCaseLetters;
instance.countSpecialCharacters = local.countSpecialCharacters;
instance.countUpperCaseLetters = local.countUpperCaseLetters;
instance.emailAddress = local.emailAddress;
instance.firstName = local.firstName;
instance.lastName = local.lastName;
instance.password = local.password;
instance.passwordLength = local.passwordLength;
instance.requireOptimalPassword = local.requireOptimalPassword;
instance.userName = local.userName;
// Perform password testing
testPassword();
// Return the "this" variable scope
return this;
}
//--------------------------------------------------------------------------
// Function : isPasswordValid()
// Type : public
// Arguments : None
// Actions : Tests password.
// Returns : Boolean; indicates whether or not the password meets the
// minimum requirements.
//--------------------------------------------------------------------------
function isPasswordValid()
{
// Return Boolean variable indicating whether or not the password meets
// the minimum requirements
return instance.isPasswordValid;
}
//--------------------------------------------------------------------------
// Function : getErrors()
// Type : public
// Arguments : None
// Actions : Tests password.
// Returns : String; an HTML list of the reasons a password does not
// meet the minimum requirements.
//--------------------------------------------------------------------------
function getErrors()
{
// Return an HTML list of the reasons a password does not meet the
// minimum requirements
return instance.errorMessages;
}
//--------------------------------------------------------------------------
// Function : getStrength()
// Type : public
// Arguments : None
// Actions : Tests password.
// Returns : String; the strength of the password (i.e., WEAK, MEDIUM,
// or STRONG).
//--------------------------------------------------------------------------
function getStrength()
{
// Return the strength of the password (i.e., WEAK, MEDIUM, or STRONG)
return instance.passwordStrength;
}
//========================================================================//
// *** PRIVATE METHODS *** //
//========================================================================//
//--------------------------------------------------------------------------
// Function : testPassword()
// Type : private
// Arguments : None
// Actions : Tests password.
// Returns : None
//--------------------------------------------------------------------------
function testPassword()
{
// If the password meets the optimal, strongest requirements...
if (instance.passwordLength gte
instance.OPTIMAL_PASSWORD_LENGTH
and
instance.countLowerCaseLetters gte
instance.OPTIMAL_LOWER_CASE_LETTERS
and
instance.countUpperCaseLetters gte
instance.OPTIMAL_UPPER_CASE_LETTERS
and
instance.countDigits gte
instance.OPTIMAL_DIGITS
and
instance.countSpecialCharacters gte
instance.OPTIMAL_SPECIAL_CHARACTERS
) {
// Set the password strength indicating that it meets the optimal,
// strongest requirements
instance.passwordStrength = "STRONG";
}
// Otherwise, if the password meets the minimum requirements...
else if (instance.passwordLength gte
instance.MIN_PASSWORD_LENGTH
and
instance.countLowerCaseLetters gte
instance.MIN_LOWER_CASE_LETTERS
and
instance.countUpperCaseLetters gte
instance.MIN_UPPER_CASE_LETTERS
and
instance.countDigits gte
instance.MIN_DIGITS
and
instance.countSpecialCharacters gte
instance.MIN_SPECIAL_CHARACTERS
) {
// Set the password strength indicating that it meets the minimum
// requirements
instance.passwordStrength = "MEDIUM";
}
// Otherwise, if the password does not even meet the minimum
// requirements...
else {
// Set the password strength indicating that it is a weak password.
instance.passwordStrength = "WEAK";
}
// If password meets requirements...
if (
(
instance.requireOptimalPassword eq false
and
(
instance.passwordStrength eq "MEDIUM"
or
instance.passwordStrength eq "STRONG"
)
)
or
(
instance.requireOptimalPassword eq true
and
instance.passwordStrength eq "STRONG"
)
) {
// Indicate that the password is valid
instance.isPasswordValid = true;
}
// Otherwise, if the password does not meet requirements...
else {
// Indicate that the password is not valid
instance.isPasswordValid = false;
// If password does not meet the minimum length...
if (
instance.requireOptimalPassword eq false
and
instance.passwordLength lt
instance.MIN_PASSWORD_LENGTH
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
Please check that the password is at least
#instance.MIN_PASSWORD_LENGTH#
#iif(instance.MIN_PASSWORD_LENGTH eq 1,
DE("character"),
DE("characters"))#
in length.
</li>
';
}
else if (
instance.requireOptimalPassword eq true
and
instance.passwordLength lt
instance.OPTIMAL_PASSWORD_LENGTH
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
A root/super administrator password must
be at least #instance.OPTIMAL_PASSWORD_LENGTH#
#iif(instance.OPTIMAL_PASSWORD_LENGTH eq 1,
DE("character"),
DE("characters"))#
in length.
</li>
';
}
// If password does not have the minimum number of lower-case
// letters...
if (
instance.requireOptimalPassword eq false
and
instance.countLowerCaseLetters lt
instance.MIN_LOWER_CASE_LETTERS
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
Please check that the password has at least
#instance.MIN_LOWER_CASE_LETTERS# lower-case
#iif(instance.MIN_LOWER_CASE_LETTERS eq 1,
DE("letter."),
DE("letters."))#
</li>
';
}
else if (
instance.requireOptimalPassword eq true
and
instance.countLowerCaseLetters lt
instance.OPTIMAL_LOWER_CASE_LETTERS
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
A root/super administrator password must have at least
#instance.OPTIMAL_LOWER_CASE_LETTERS# lower-case
#iif(instance.OPTIMAL_LOWER_CASE_LETTERS eq 1,
DE("letter."),
DE("letters."))#
</li>
';
}
// If password does not have the minimum number of upper-case
// letters...
if (
instance.requireOptimalPassword eq false
and
instance.countUpperCaseLetters lt
instance.MIN_UPPER_CASE_LETTERS
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
Please check that the password has at least
#instance.MIN_UPPER_CASE_LETTERS# upper-case
#iif(instance.MIN_UPPER_CASE_LETTERS eq 1,
DE("letter."),
DE("letters."))#
</li>
';
}
else if (
instance.requireOptimalPassword eq true
and
instance.countUpperCaseLetters lt
instance.OPTIMAL_UPPER_CASE_LETTERS
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
A root/super administrator password must have at least
#instance.OPTIMAL_UPPER_CASE_LETTERS# upper-case
#iif(instance.OPTIMAL_UPPER_CASE_LETTERS eq 1,
DE("letter."),
DE("letters."))#
</li>
';
}
// If password does not have the minimum required numbers...
if (
instance.requireOptimalPassword eq false
and
instance.countDigits lt instance.MIN_DIGITS
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
The password must have at least #instance.MIN_DIGITS#
#iif(instance.MIN_DIGITS eq 1,
DE("digit."),
DE("digits."))#
</li>
';
}
else if (
instance.requireOptimalPassword eq true
and
instance.countDigits lt instance.OPTIMAL_DIGITS
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
A root/super administrator password must have at least
#instance.OPTIMAL_DIGITS#
#iif(instance.OPTIMAL_DIGITS eq 1,
DE("digit."),
DE("digits."))#
</li>
';
}
// If password does not have the minimum required special
// characters...
if (
instance.requireOptimalPassword eq false
and
instance.countSpecialCharacters lt
instance.MIN_SPECIAL_CHARACTERS
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
Please make sure that the password has at least
#instance.MIN_SPECIAL_CHARACTERS# special
#iif(instance.MIN_SPECIAL_CHARACTERS eq 1,
DE("character."),
DE("characters."))#
Special characters may include any of the following:
<blockquote>
~ ! @ ## $ % ^ &amp; * ( ) _ + ` { } | : &lt; &gt;
? [ ] \ ; " , . /
</blockquote>
</li>
';
}
else if (
instance.requireOptimalPassword eq true
and
instance.countSpecialCharacters lt
instance.OPTIMAL_SPECIAL_CHARACTERS
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
A root/super administrator password must have at least
#instance.OPTIMAL_SPECIAL_CHARACTERS# special
#iif(instance.OPTIMAL_SPECIAL_CHARACTERS eq 1,
DE("character."),
DE("characters."))#
Special characters may include any of the following:
<blockquote>
~ ! @ ## $ % ^ &amp; * ( ) _ + ` { } | : &lt; &gt; ?
[ ] \ ; " , . /
</blockquote>
</li>
';
}
// If password does not have the minimum required special
// characters...
if (
instance.requireOptimalPassword eq false
and
(
(instance.MIN_SPECIAL_CHARACTERS + instance.MIN_DIGITS) lt
instance.MIN_NON_LETTER_CHARACTERS
)
and
(
(instance.countSpecialCharacters + instance.countDigits) lt
instance.MIN_NON_LETTER_CHARACTERS
)
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
Please make sure that the password has at least
#instance.MIN_NON_LETTER_CHARACTERS# non-letter
#iif(instance.MIN_NON_LETTER_CHARACTERS eq 1,
DE("character."),
DE("characters."))#
</li>
';
}
else if (
instance.requireOptimalPassword eq true
and
(
(
instance.OPTIMAL_SPECIAL_CHARACTERS
+ instance.OPTIMAL_DIGITS
) lt instance.OPTIMAL_NON_LETTER_CHARACTERS
)
and
(
(
instance.countSpecialCharacters
+ instance.countDigits
) lt instance.OPTIMAL_NON_LETTER_CHARACTERS
)
) {
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
A root/super administrator password must have at least
#instance.OPTIMAL_NON_LETTER_CHARACTERS# non-letter
#iif(instance.OPTIMAL_NON_LETTER_CHARACTERS eq 1,
DE("character."),
DE("characters."))#
</li>
';
}
}
// ===============================
// Test for criteria in which to
// reject passwords outright.
// ===============================
// If there is a space...
if (find(" ", instance.password)) {
// Reject password
instance.isPasswordValid = false;
instance.passwordStrength = "WEAK";
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
The password cannot contain a space.
</li>
';
}
// If there is a single-quote...
if (find("'", instance.password)) {
// Reject password
instance.isPasswordValid = false;
instance.passwordStrength = "WEAK";
// Specify the reason
instance.errorMessages = instance.errorMessages & "
<li>
The password cannot contain a single-quote character
[ <em>'</em> ].
</li>
";
}
// Get first and last instance of non-letter characters
firstNonLetterCharIndex = reFind("[0-9\W]", instance.password);
lastNonLetterCharIndex = reFind("[0-9\W]", reverse(instance.password));
charIndex = reFind("[A-Za-z]",instance.password);
// This check only applies for passwords that already contain a number
// or special character...
if (charIndex gt 0
and
firstNonLetterCharIndex gt 0) {
// Extract all characters after first instance of non-letter
if (firstNonLetterCharIndex lt instance.passwordLength
and
firstNonLetterCharIndex gt 0
) {
sectionAfterFirstNonLetterChar
= right(instance.password,
(instance.passwordLength +1)
- firstNonLetterCharIndex);
// Look for letters after first instance of non-letter
local.letterIndex = reFind("[A-Za-z]",
sectionAfterFirstNonLetterChar);
// If there are none...
if (local.letterIndex eq 0) {
// Reject password
instance.isPasswordValid = false;
instance.passwordStrength = "WEAK";
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
The password cannot have all numbers and/or special
characters at the end.
</li>
';
}
}
}
if (charIndex gt 0
and
lastNonLetterCharIndex gt 0) {
// Extract all characters before last instance of non-letter
if (lastNonLetterCharIndex lt instance.passwordLength
and
lastNonLetterCharIndex gt 0
) {
sectionBeforeLastNonLetterChar
= right(reverse(instance.password),
(instance.passwordLength +1)
-lastNonLetterCharIndex);
// Look for letters before last instance of non-letter
local.letterIndex = reFind("[A-Za-z]",
sectionBeforeLastNonLetterChar);
// If there are none...
if (local.letterIndex eq 0) {
// Reject password
instance.isPasswordValid = false;
instance.passwordStrength = "WEAK";
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
The password cannot have all numbers and/or special
characters at the beginning.
</li>
';
}
}
}
// Extract the name from the email address
if (find("@", instance.emailAddress) gt 1) {
emailAddressName = mid(instance.emailAddress,
1,
find("@", instance.emailAddress)-1
);
}
else {
emailAddressName = instance.emailAddress;
}
// If the password is the same as the user name or email address...
if (
(
len(trim(emailAddressName)) gt 0
and
findNoCase(emailAddressName, instance.password)
)
or
(
len(trim(instance.userName)) gt 0
and
findNoCase(instance.userName, instance.password)
)
or
(
len(trim(instance.firstName)) gt 0
and
findNoCase(instance.firstName, instance.password)
)
or
(
len(trim(instance.lastName)) gt 0
and
findNoCase(instance.lastName, instance.password)
)
) {
// Reject password
instance.isPasswordValid = false;
instance.passwordStrength = "WEAK";
// Specify the reason
instance.errorMessages = instance.errorMessages & '
<li>
The password cannot contain any personally
identifying information already associated with
your account.
</li>
';
}
}
</cfscript>
</cfcomponent>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment