Advertisement

Build a Simple Password Strength Checker

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

Providing instant feedback is the in-thing right now. Why limit yourself to checking usernames and email addresses? Why not extend this to provide quick visual feedback about the strength of the password the user has input? Today, we'll take a look at how to create a simple password strength checker using the jQuery library, regular expressions and a simple algorithm.


A Word From the Author

As most security experts will tell you, the user is always the weakest link. The most secure of systems are vulnerable when a user chooses an extremely ill-advised password. With that in mind, the recent trend seems to be providing quick feedback to the user regarding the strength of the password so the user can extend or modify the password to make it more secure.

Hovers

Today, we are going to use the jQuery library, a bunch of regular expressions and a very simple algorithm to create a basic password strength checker. Interested? Let's get started right away! Here is a demo of what we are trying to build today:

Demo image

Design Goals

Our design goals for this specific functionality is relatively small.

  • Provide visual feedback to the user regarding the strength of their password.
  • The feedback has to be instantaneous. This means no clicking on a button to test the strength.
  • The trigger event can be any of the key board events. I've chosen keyup since this is the most appropriate for our specific need.
  • For the visual feedback, modifying the text alone, while useful, is severely lacking. I've chosen to change the background colors as well to draw the user's attention to this.
  • Provide additional quantifiable feedback so the user knows in which departments the password lacks strength and how it can be improved.

Now that we've adequately figured out our needs, we can move on to the next stage.

Plan of Action

We'll now decide on the order of the individual steps that need to be done.

  • Hook up the event handler to the keyup event of the input box.
  • Let the event handler check the input but delegate everything else to individual helper methods.
  • The helper methods should take care of parsing the input and analyzing it, computing the complexity and printing out the results.
  • Make sure the event handlers fires off the helper methods only if the length of the input is greater than the expected minimum so as to not waste CPU cycles on invalid entries.
  • Return control to the event handler in case anything else needs to be done.

The Algorithm

The Algorithm

In in the interest of keeping this write up succinct and approachable, I've decided to go with a very basic algorithm. The algorithm analyzes the string, gives bonuses for extra length, presence of numbers, symbols and upper case letters and penalties for letter or number only inputs. We aren't going to look at matching common patterns or checking the input against a dictionary since this is out of the scope of the article. If interest peaks, I may do an article about this in the future.

First we check the length of the input string. If it's greater than the minimum length, give it a base score of 50. Else make it 0. Next iterate through each character of the string and check if it is a symbol, number or upper case letter. If so, make a note of it.

Then check how many extra characters the string has, over the recommended minimum and grant a bonus for each character. Also grant a bonus if the string contains a combination of upper case letters, numbers and symbols or all three. Grant a bonus for each's presence too.

Check if the string only contains either lower case letters or numbers and if so, penalize.

Add up all the numbers and decide the strength of the password accordingly.

That's the long and short of the algorithm. It's not going exceedingly suave but it catches a lot of bad passwords. You'll understand this better once we see it in code.

Core Markup

The HTML markup of the demo page looks like so:

<!DOCTYPE html>
<html lang="en-GB">
<head>
<title>Simple Password Strength Checker - by Siddharth for NetTuts</title>
<link type="text/css" href="css/style.css" rel="stylesheet" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/mocha.js"></script>
</head>
<body>

<div id="container">

<h1>Create a simple password strength checker</h1>

<h2 class="bolded">by Siddharth for the lovely folks at Net Tuts</h2>

<p>Type in your password to get visual feedback regarding the strength of your password.</p>
<p>I assure you, I am not stealing your passwords. The form doesn't not submit. You can look through the source if you are suspicious. :)</p> 

<div class="block">
<input id="inputPassword"/>
<div id="complexity" class="default">Enter a random value</div>
</div>

<div class="block">
<div id="results" class="default">Breakdown of points</div>
<div id="details"></div>
</div>

</div>
</body>
</html>

Disregard all the usual markup. Do notice the input element with an ID of inputPassword, the div element with an ID of complexity which shows the complexity of the password and div element with an ID of details which shows the breakdown of points.

We've also included the jQuery library and our own script file. Extra points if you appreciate the name of our script file.

CSS Styling

body{
	font-family: "Lucida Grande", "Verdana", sans-serif;
}

h1{
	font-size: 30px;
	padding: 0;
	margin: 0;
}

h2{
	font-size: 18px;
	padding: 0;
	margin: 0 5px 30px 0;
}

input{
	width: 288px;
	height: 30px;
	margin: 50px 0 0 0;
	padding: 3px 5px;
	font-size: 22px;
	font-family: "Lucida Grande", "Verdana", sans-serif;
}

#container{
	width: 820px;
	margin-left: auto;
	margin-right: auto;
	padding: 50px 0 0 0;
}

.block{
	width: 300px;
	margin: 0 auto 0 auto;
}

#complexity, #results{
	width: 300px;
	padding: 3px 0;
	height: 20px;
	color: #000;
	font-size: 14px;
	text-align: center;
}

#results{
	margin: 30px 0 20px 0;
}

.default{background-color: #CCC;}
.weak{background-color: #FF5353;}
.strong{background-color: #FAD054;}
.stronger{background-color: #93C9F4; }
.strongest{background-color: #B6FF6C;}

span.value{
	font-weight:bold;
	float: right;
}

Just boiler plate CSS for layouts and typography. We do have a bunch of classes at the bottom for each individual strength ratings. We'll add them to the elements when needed.

JavaScript Implementation

Now that we have a solid framework and some basic styling in place, we can start coding up the required functionality. Do note that we make extensive use of jQuery. Feel free to link to Google's CDN if necessary.

Variables and Event Handling

Since a lot of number juggling is going to go on, we need a bunch of variables to hold the values. Since is a demo and not production code, I'd decided to declare the variables as global and access them through the helper methods instead of declaring them internally and then passing it to the functions.

	var strPassword;
	var charPassword;
	var complexity = $("#complexity");
	var minPasswordLength = 8;
	var baseScore = 0, score = 0;
	
	var num = {};
	num.Excess = 0;
	num.Upper = 0;
	num.Numbers = 0;
	num.Symbols = 0;

	var bonus = {};
	bonus.Excess = 3;
	bonus.Upper = 4;
	bonus.Numbers = 5;
	bonus.Symbols = 5;
	bonus.Combo = 0; 
	bonus.FlatLower = 0;
	bonus.FlatNumber = 0;

The variable names are pretty standard fare but I'd give a rundown anyway. strPassword holds the input box's value, charPassword is an array holding each character of the string, complexity holds a reference to the div element. We also define the minimum password length, score and the base score.

We create a quick hash to hold the number of extra characters, upper case characters, numbers and symbols. We do the same for the bonuses. The num hash holds the number of characters while the bonus hash holds the bonus multipliers. You can just create individual variables but I think this looks cleaner.

Don't forget to hook up the event handler to the event.

	$("#inputPassword").bind("keyup", checkVal);

checkVal is the event handler which we'll create in just a tiny bit.

The Event Handler

function checkVal()
{
	if (charPassword.length >= minPasswordLength)
	{
		baseScore = 50;	
		analyzeString();	
		calcComplexity();		
	}
	else
	{
		baseScore = 0;
	}
	
	outputResult();
}

We first check the length of the input string. If it's greater than or equal to the minimum specified length, we can proceed. We set the base score to 50 and call the helper methods which take care of analyzing the string and computing it's complexity.

If it's less than the expected length, we just set the base score to 0.

We then call the outputResult function which takes care of making sense of the computed computations. We'll see how it works later below.

Analyzing the Input

function analyzeString ()
{	
	for (i=0; i<charPassword.length;i++)
	{
		if (charPassword[i].match(/[A-Z]/g)) {num.Upper++;}
		if (charPassword[i].match(/[0-9]/g)) {num.Numbers++;}
		if (charPassword[i].match(/(.*[!,@,#,$,%,^,&,*,?,_,~])/)) {num.Symbols++;} 
	}
	
	num.Excess = charPassword.length - minPasswordLength;
	
	if (num.Upper && num.Numbers && num.Symbols)
	{
		bonus.Combo = 25; 
	}

	else if ((num.Upper && num.Numbers) || (num.Upper && num.Symbols) || (num.Numbers && num.Symbols))
	{
		bonus.Combo = 15; 
	}
	
	if (strPassword.match(/^[\sa-z]+$/))
	{ 
		bonus.FlatLower = -15;
	}
	
	if (strPassword.match(/^[\s0-9]+$/))
	{ 
		bonus.FlatNumber = -35;
	}
}

This maybe look a bit complicated but I promise you, it's only because of the regular expressions. Let's go over the code part by part.

First, we need to figure out the composition of the string in question. As in, we need to figure out whether the string contains upper case letters, numbers of symbols and if so, how many of them are present. With this in mind, we iterate through the character array and check each characters to see its type. The match method lets us match a string against a regular expression. If you are new to regular expression, I suggest you read Vasili's great article here.

Next, we have determine the difference between the length of the input string and the specified minimum length of the password. This gives us the excess number of characters to play around with.

We then check if the string has upper case, numbers and symbols. If so, grant a bonus. We also check to see whether it has combinations of two of them and grant a smaller bonus if so.

Finally, we check to see whether a string is flat: whether it contains only lower case letters or only numbers. We check this with a regular expression and if so, penalize the password for this practice.

Calculate the Complexity

function calcComplexity()
{
	score = baseScore + (num.Excess*bonus.Excess) + (num.Upper*bonus.Upper) + (num.Numbers*bonus.Numbers) + 
(num.Symbols*bonus.Symbols) + bonus.Combo + bonus.FlatLower + bonus.FlatNumber;	
}

Just a simple addition. We add the base score to the product of the number of excess characters and it's multiplier. Same for upper case letters, numbers and symbols. We then add a bonus for combinations, if present, and add penalties if the string is flat.

Updating the UI

Now that all the computation is behind us, we can update the UI to reflect the changes. Here are each of the states.

Points
Points
Points
Points
function outputResult()
{
	if ($("#inputPassword").val()== "")
	{ 
		complexity.html("Enter a random value").addClass("default");
	}
	else if (charPassword.length < minPasswordLength)
	{
		complexity.html("At least " + minPasswordLength+ " characters please!").addClass("weak");
	}
	else if (score<50)
	{
		complexity.html("Weak!").addClass("weak");
	}
	else if (score>=50 && score<75)
	{
		complexity.html("Average!").addClass("strong");
	}
	else if (score>=75 && score<100)
	{
		complexity.html("Strong!").addClass("stronger");
	}
	else if (score>=100)
	{
		complexity.html("Secure!").addClass("strongest");
	}
}

Nothing fancy here but we'll go through it line by line.

We first check to see whether input is empty. If so, change the result's text and add a default class to change it's background color back to it's original gray.

If it's less than the minimum specified length, we change the text accordingly and add a weak class so it's background is red. We do the same if the total score is less than 50 but change the text to weak.

As the score increases, we change the text accordingly and add the necessary classes. Feel free to change the baseline scores for each rating. I just put in unscientific values to get the demo going.

Updating the Detailed Breakdown

Points

With the main result updated, we can look at updating the stats now.

function outputResult()
{
	// Previous Code
    
    $("#details").html("Base Score :<span class=\"value\">" + baseScore  + "</span>"
				  + "<br />Length Bonus :<span class=\"value\">" + (num.Excess*bonus.Excess) + " ["+num.Excess+"x"+bonus.Excess+"]</span> " 
				  + "<br />Upper case bonus :<span class=\"value\">" + (num.Upper*bonus.Upper) + " ["+num.Upper+"x"+bonus.Upper+"]</span> "
				  + "<br />Number Bonus :<span class=\"value\"> " + (num.Numbers*bonus.Numbers) + " ["+num.Numbers+"x"+bonus.Numbers+"]</span>"
				  + "<br />Symbol Bonus :<span class=\"value\"> " + (num.Symbols*bonus.Symbols) + " ["+num.Symbols+"x"+bonus.Symbols+"]</span>"
				  + "<br />Combination Bonus :<span class=\"value\"> " + bonus.Combo + "</span>"
				  + "<br />Lower case only penalty :<span class=\"value\"> " + bonus.FlatLower + "</span>"
				  + "<br />Numbers only penalty :<span class=\"value\"> " + bonus.FlatNumber + "</span>"
				  + "<br />Total Score:<span class=\"value\"> " + score  + "</span>" 
}

This part is not as confusing as it looks. Let me explain.

Instead of just updating the individual values for the detailed results, I've resorted to just updating the complete HTML value of the container. I know it is going to be sluggish when a number of these boxes add up but accessing each element individually and then updating it's value for a tiny demo seemed to be rather counter-productive. So run with me here.

This is just like injecting regular HTML into an element except that we've placed a couple variables inside to enable the details to be updated instantaneously. Each value gets a value class to make it bold. We also display the number of special characters and it's multipler so the user can gauge which elements get more weightage.

A few Tweaks

At this point of time, there are 2 bugs which show up.

  • If you type in a long password and then erase the text box, the background colors don't change back.
  • In the same scenario, the details of the points break down don't update as it should.

We'll tackle them one by one.

For the first bug, the root cause happens to be the fact that we don't remove all the other classes. This wouldn't be a problem if the most recently added classes takes precedence over others. Unfortunately, it's not so. Here is a quick fix.

function outputResult()
{
	if ($("#inputPassword").val()== "")
	{ complexity.html("Enter a random value").removeClass("weak strong stronger strongest").addClass("default");}
	else if (charPassword.length < minPasswordLength)
	{complexity.html("At least " + minPasswordLength+ " characters please!").removeClass("strong stronger strongest").addClass("weak");}
	else if (score<50)
	{complexity.html("Weak!").removeClass("strong stronger strongest").addClass("weak");}
	else if (score>=50 && score<75)
	{complexity.html("Average!").removeClass("stronger strongest").addClass("strong");}
	else if (score>=75 && score<100)
	{complexity.html("Strong!").removeClass("strongest").addClass("stronger");}
	else if (score>=100)
	{complexity.html("Secure!").addClass("strongest");}

	// Details updating code
}

You are probably asking why we don't remove each and every class here. The answer is simple: we take advantage of one of the primary attributes of CSS: cascading. If you note the order of declaration of each class in the CSS file you'll notice that default occurs the first and strongest comes the last which means if an element has the strongest class it'll override any modifications made by any class above it. So we'll have to only remove classes which occur below the relevant class. For example, for an element to have strong, we'll have to remove the stronger and strongest classes.

The reason the second bug exist is due to the fact that the individuals variables aren't reset when a new event occurs. They carry over to the next event as well. In order to fix this, we create a quick function which reinitializes all the relevant variables and add it the checkVal event handler so it is called every time the input box's text is updated.

function init()
{
	strPassword= $("#inputPassword").val();
	charPassword = strPassword.split("");
		
	num.Excess = 0;
	num.Upper = 0;
	num.Numbers = 0;
	num.Symbols = 0;
	bonus.Combo = 0; 
	bonus.FlatLower = 0;
	bonus.FlatNumber = 0;
	baseScore = 0;
	score =0;
}
function checkVal()
{
	init();
    
	// Other code
}

Limitations

Limitation with the current implementation

If you've been playing around with the demo a bit, you'll notice that Pa$$W0rd$ turns up as a secure password while in fact it'll be broken pretty soon. This is due to the simplicity of our algorithm here. We don't check for character replacements. Or common passwords or patterns for that matter. Doing such things would increase the difficulty of this tutorial while reducing its approachability, both of which I didn't want for this particular write up.

This is intended as a basic password strength checker. If you need to beef it up, you could probably add a couple more regular expressions to check for patterns and character repetition and then tune the results accordingly.

Looking the input up against a dictionary is really out of the scope of this article and would require either a huge dictionary downloaded to the client side or hooking it up to a server side system to do that. Again, I really wanted to avoid both of them this time.

Conclusion

And there you have it: how to add a user friendly functionality, the ability to let the user know the strength of a password he just entered, to your projects. Hopefully you've found this tutorial interesting and this has been useful to you. Feel free to reuse this code elsewhere in your projects and chime in here if you are running into difficulties.

Questions? Nice things to say? Criticisms? Hit the comments section and leave me a comment. Happy coding!


Advertisement