Javascript Password Strength Meter
- Blogs:
What makes a strong password? This quick and dirty password strength meter is meant to help users learn how to create stronger passwords. Because it's written in Javascript the password is never sent over the network. Feel free to audit the code and recommend some better regular expressions, weightings, or bug fixes by submitting a comment.
Tips for strong passwords:
- Make your password 8 characters or more
- Use mixed case letters (upper and lower case)
- Use more than one number
- Use special characters (!,@,#,$,%,^,&,*,?,_,~)
- Use L33t
- Use a random password generator/password vault like Password Safe or pwsafe
- Use PasswordMaker
- geekwisdom's blog
- Login or register to post comments


Comments
Great
Very usefull tips
Different approach to password strength calculation
I could see it in my mind: a kind of frequency spectrum bar-chart of password components pulsing up and down like the graphic VU meter on a DJ's mixing panel. The height of the vertical bars represents the number of lowercase, uppercase, numbers and special characters present in the password as it is typed by an unsuspecting User--a password histogram. If the bars are more or less the same height the password is stronger than if the heights of the bars vary. The more irregular the heights are, the weaker is the password.
Also, as the password length increases, the overall height of the bars increases. Thus, the taller the meter, the stronger, yet, is the password. In my gut I knew there was a way to express this mathematically and after fiddling around with it, I drew the bars on a piece of paper, and that's when I saw it: each bar is an area (height = count, width = 1), and each area is a face on a crazy 4-dimensional solid. Multiply the areas together and you get a volume. The larger the volume, the stronger the password. All that is left is to determine the thresholds for the various degrees of strength.
It's easier to see in 3 dimensions. Picture a hexahedron with rectangular faces. It has a height, a width and a depth. Assign the uppercase count to the height, the numbers count to the width, and the special chars count to the depth. If all the counts are equal, then the shape is a cube. If, the counts vary, then the shape is composed of rectangles. Say the password is 9 characters long, and it has 3 uppercase, 3 numbers and 3 special chars. The volume is 3*3*3 or 27. Now, suppose the counts are: 7 uppercase, 1 number and 1 special char. The volume becomes a long thin sort of square rod kind of solid with a volume of 7*1*1 or 7 (much less than 27). How about something in the middle: 5 uppercase, 2 numbers and 2 special chars. That's 5*2*2 or 20, which is still less than 27. A cube is always going to have the greatest volume.
This also includes a "runs" detector. It's basically a digital hi-pass filter (or edge detector) with rectification (absolute zero applied to each difference). See comment on the detectRuns() method for details (this, almost certainly doesn't work with Unicode.). This, actually, could be used to generate an overall password strength, if the various char ranges were weighted to produce filterable edge intensities.
Here's the listing:
/** * Determines the strength of a given password based on * frequency of of occurrence of lowercase, uppercase, * numbers and the special characters passed via the spc_chars * argument. The more even the spread of occurrences, the * stronger the password. * * This class contains the following public parameters: * 'lcase_count' : lowercase occurrence count. * 'ucase_count' : uppercase occurrence count. * 'num_count' : number occurrence count. * 'schar_count' : special character occurrence count. * 'length' : length of password string. * 'strength' : strength value of password. * 'verdict' : textual strength indication * ['weak', 'medium', 'strong']. * * @param string arg_password The password * @param string arg_spc_chars A string of special characters * to search for in the password. By making this an * argument, the range of special characters can be * controlled externally. * @return string The verdict as 'Weak'|'Medium'|'Strong' */ function Password(arg_password, arg_spc_chars) { var password = arg_password; var spc_chars = arg_spc_chars; this.lcase_count = 0; this.ucase_count = 0; this.num_count = 0; this.schar_count = 0; this.length = 0; this.strength = 0; this.runs_score = 0; this.verdict = ''; // These numbers are just guesses on my part (and not // all that educated, either ;) Adjust accordingly. var verdict_conv = {'weak':2.7, 'medium':53, 'strong':150}; // These are weighting factors. I figure that adding // numbers is a little better than adding uppercase // because numbers probably are not vulnerable to // dictionary searches, and adding special chars is // even better. These factors provide yet another // dimension. Again, there are only guesses. var flc = 1.0; // lowercase factor var fuc = 1.0; // uppercase factor var fnm = 1.3; // number factor var fsc = 1.5; // special char factor this.getStrength = function() { if ((this.run_score = this.detectRuns()) <= 1) { return "Very weak"; } var regex_sc = new RegExp('['+spc_chars+']', 'g'); this.lcase_count = password.match(/[a-z]/g); this.lcase_count = (this.lcase_count) ? this.lcase_count.length : 0; this.ucase_count = password.match(/[A-Z]/g); this.ucase_count = (this.ucase_count) ? this.ucase_count.length : 0; this.num_count = password.match(/[0-9]/g); this.num_count = (this.num_count) ? this.num_count.length : 0; this.schar_count = password.match(regex_sc); this.schar_count = (this.schar_count) ? this.schar_count.length : 0; this.length = password.length; var avg = this.length / 4; // I'm dividing by (avg + 1) to linearize the strength a bit. // To get a result that ranges from 0 to 1, divide // by Math.pow(avg + 1, 4) this.strength = ((this.lcase_count * flc + 1) * (this.ucase_count * fuc + 1) * (this.num_count * fnm + 1) * (this.schar_count * fsc + 1)) / (avg + 1); if (this.strength > verdict_conv.strong) this.verdict = 'Strong'; else if (this.strength > verdict_conv.medium) this.verdict = 'Medium'; else if (this.strength > verdict_conv.weak) this.verdict = 'Weak'; else this.verdict = "Forget it!"; return this.verdict; } // This is basically an edge detecter with a 'rectified' (or // absolute zero) result. The difference of adjacent equivalent // char values is zero. The greater the difference, the higher // the result. 'aaaaa' sums to 0. 'abcde' sums to 1. 'acegi' // sums to 2, etc. 'aaazz', which has a sharp edge, sums to // 6.25. Any thing 1 or below is a run, and should be considered // weak. this.detectRuns = function() { var parts = password.split(''); var ords = new Array(); for (i in parts) { ords[i] = parts[i].charCodeAt(0); } var accum = 0; var lasti = ords.length-1 for (var i=0; i < lasti; ++i) { accum += Math.abs(ords[i] - ords[i+1]); } return accum/lasti; } this.toString = function() { return 'lcase: '+this.lcase_count+ ' -- ucase: '+this.ucase_count+ ' -- nums: '+this.num_count+ ' -- schar: '+this.schar_count+ ' -- strength: '+this.strength+ ' -- verdict: '+this.verdict; } }Usage:
function checkPassword() { var special_chars = "~!@#$%&*"; var pw = new Password(document.getElementById('da_password').value, special_chars); var verdict = pw.getStrength(); var hint = ''; if (pw.ucase_count == 0) hint += "Try adding some uppercase letters. "; if (pw.num_count == 0) hint += "Try adding some numbers. "; if (pw.schar_count == 0) hint += "Try adding one or more of the following characters: "+ special_chars+"."; if (pw.run_score <= 1) hint += "Avoid runs (e.g. 'aaaa', 'efghi', '1234'). "; element = document.getElementById("hint"); element.innerHTML = hint; element = document.getElementById("strength"); element.innerHTML = verdict; }The HTML:
<input type="text" id="da_password" onkeyup="checkPassword()"/> <div id="hint"></div> <div id="strength"></div>Missing is a dictionary look-up. Also, certain special chars will break this (such as '^').
some work on the regex
Hi, found the code while googlin' for a JS password checker - nice one, and useful for my purposes. So, I decided to adopt it for my project, with some minor mods, especially by re-writing the regex.
Ah, one word to the combination changes: I decided not to implement them, because IMHO all these are checked beforehand and don't really add strength.
Well, here's my version. It's written as a JS function, and just returning the strength, since this is what I needed - maybe someone can use it.
checkpwd: function(password) {
/*function to calculate "strength" of a password
* expects password, returns strength
*/
//calculate strength out of length
var strength = 0;
var length = password.length;
if (length > 16) {
strength = 18;
} else {
if (length > 8) {
strength = 12
} else {
if (length > 5) {
strength = 6;
} else {
if (length > 0) {
strength = 3;
} else {
return 0; //no password means no strength, :-)
}
}
}
}
//Now consider the letters
if (password.match(/[a-z]/i)) {
strength += 5; //at least one letter, 5 points
if (password.match(/[a-z][A-Z]|[A-Z][a-z]/)) {
strength += 2; //mixed-case adds 2 points
}
} //no letter, no points
//Now check for numbers
count = password.match(/[0-9]/g);
if (count) {
strength += 5; //at least one number, 5 points
if (count > 2) {
strength += 2; //three or more numbers, 2 extra points;
}
} //no number, no points
//Now check for special characters
count = password.match(/[!,@,#,$,%,^,*,?,_,~,-]/g)
if (count) {
strength += 5; //at least one special characters, 5 points
if (count > 1) {
strenth += 2; //two or more special characters, 2 extra points
}
} //no special characters, no points
return strength;
}
Flex version
http://www.mattholden.com/dslabs/PasswordEntry.swf (demo)
http://www.mattholden.com/dslabs/PasswordEntry.mxml (code) Thanks!
great, if not for the bugs still in it!
Submitted by Jaeden on Wed, 2008-06-25 22:22.
Used your scoring system (with some minor modifications) to create a Flex/Flash component, also MIT licensed. Check it out...
http://www.mattholden.com/dslabs/PasswordEntry.swf (demo)
http://www.mattholden.com/dslabs/PasswordEntry.mxml (code)
Even with all the alterations afterwards, one simple problem remains: IE COPY AND PASTE SCREWS IT UP
probably 50% of the time people will instead of re-typing the password, will copy / paste it. And with your code, when they do, the whole page gets screwed, the status bar goes haywire and jumps slap to the right going off the edge, etc...
great code, just needs tweaked.
passwordmeter.js
A rather trivial submission but saves CPU cycles.
if(intScore < 16)
{
strVerdict = "very weak"
}
else if (intScore < 25)
{
strVerdict = "weak"
}
else if (intScore < 35)
{
strVerdict = "mediocre"
}
else if (intScore < 45)
{
strVerdict = "strong"
}
else
{
strVerdict = "stronger"
}
a dictionnary would be nice...
I was gonna look at that
Weighting
Erm... I'm talking rubbish.
This page has some
http://www.lockdown.co.uk/?pg=combi I'll work out some better weighing from this.
From that analysis...
<head>
<script language="JavaScript1.1">
<!-- Begin
/* ************************************************************
Created: 20060120
Author: Steve Moitozo
Description: This is a quick and dirty password quality meter
written in JavaScript so that the password does
not pass over the network
Revision Author: Dick Ervasti (dick dot ervasti at quty dot com)
Revision Description: Exchanged text based prompts for a graphic thermometer
Revision Author: Matt Culverwell (zebadger@hotmail.com)
Revision Description: Fixed regular expressions
Revision Author: Matt Culverwell (zebadger@hotmail.com)
Revision Description: Added brand new weighting based on figures from http://www.lockdown.co.uk/?pg=combi
************************************************************ */
function testPassword(passwd)
{
var description = new Array();
description[0] = "<table><tr><td><table cellpadding=0 cellspacing=2><tr><td height=4 width=30 bgcolor=#ff0000></td><td height=4 width=120 bgcolor=tan></td></tr></table></td><td> <b>Weakest</b></td></tr></table>";
description[1] = "<table><tr><td><table cellpadding=0 cellspacing=2><tr><td height=4 width=60 bgcolor=#990000></td><td height=4 width=90 bgcolor=tan></td></tr></table></td><td> <b>Weak</b></td></tr></table>";
description[2] = "<table><tr><td><table cellpadding=0 cellspacing=2><tr><td height=4 width=90 bgcolor=#990099></td><td height=4 width=60 bgcolor=tan></td></tr></table></td><td> <b>Improving</b></td></tr></table>";
description[3] = "<table><tr><td><table cellpadding=0 cellspacing=2><tr><td height=4 width=120 bgcolor=#000099></td><td height=4 width=30 bgcolor=tan></td></tr></table></td><td> <b>Strong</b></td></tr></table>";
description[4] = "<table><tr><td><table><tr><td height=4 width=150 bgcolor=#0000ff></td></tr></table></td><td> <b>Strongest</b></td></tr></table>";
description[5] = "<table><tr><td><table><tr><td height=4 width=150 bgcolor=tan></td></tr></table></td><td> <b>Begin Typing</b></td></tr></table>";
var base = 0
var combos = 0
if (passwd.match(/[a-z]/))
{
base = (base+26);
}
if (passwd.match(/[A-Z]/))
{
base = (base+26);
}
if (passwd.match(/\d+/))
{
base = (base+10);
}
if (passwd.match(/[>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/))
{
base = (base+33);
}
combos=Math.pow(base,passwd.length);
if(combos == 1)
{
strVerdict = description[5];
}
else if(combos > 1 && combos < 1000000)
{
strVerdict = description[0];
}
else if (combos >= 1000000 && combos < 1000000000000)
{
strVerdict = description[1];
}
else if (combos >= 1000000000000 && combos < 1000000000000000000)
{
strVerdict = description[2];
}
else if (combos >= 1000000000000000000 && combos < 1000000000000000000000000)
{
strVerdict = description[3];
}
else
{
strVerdict = description[4];
}
document.getElementById("Words").innerHTML= (strVerdict);
}
// End-->
</script>
</head>
<body>
<table><tr valign=top><td><form name="commandForm">
Type password: <input type=password size=30 maxlength=50 name=password onkeyup="testPassword(document.forms.commandForm.password.value);" value="">
<br/><font color="#808080">Minimum 6 Characters</td><td><font size="1"> Password Strength:</font><a id="Words"><table><tr><td><table><tr><td height=4 width=150 bgcolor=tan></td></tr></table></td><td> <b>Begin Typing</b></td></tr></table></a></td></tr></table>
</td></tr></table>
</form>
<pre>
This works by working out how many sets of characters you have used from
<UL>
<LI>Upper (set of 26)</LI>
<LI>Lower (set of 26)</LI>
<LI>Numeric (set of 10)</LI>
<LI>Symbols (set of 33)</LI>
</UL>
It then works out the number of combinations of passwords that could be made based on the sets that you have used and the password length.
Weakest < 1000000
Weak < 1000000000000
Improving < 1000000000000000000
Strong < 1000000000000000000000000
Strongest >=1000000000000000000000000
</pre>
</body>
</html>
I've written the above code
Fixed Regex
// LETTERS
if (passwd.match(/[a-z]/)) // [verified] at least one lower case letter
{
intScore = (intScore+1)
} if (passwd.match(/[A-Z]/)) // [verified] at least one upper case letter
{
intScore = (intScore+5)
} // NUMBERS
if (passwd.match(/\d+/)) // [verified] at least one number
{
intScore = (intScore+5)
} if (passwd.match(/(\d.*\d.*\d)/)) // [verified] at least three numbers
{
intScore = (intScore+5)
} // SPECIAL CHAR
if (passwd.match(/[!,@#$%^&*?_~]/)) // [verified] at least one special character
{
intScore = (intScore+5)
} if (passwd.match(/([!,@#$%^&*?_~].*[!,@#$%^&*?_~])/)) // [verified] at least two special characters
{
intScore = (intScore+5)
} // COMBOS
if (passwd.match(/[a-z]/) && passwd.match(/[A-Z]/)) // [verified] both upper and lower case
{
intScore = (intScore+2)
} if (passwd.match(/\d/) && passwd.match(/\D/)) // [verified] both letters and numbers
{
intScore = (intScore+2)
} // [Verified] Upper Letters, Lower Letters, numbers and special characters
if (passwd.match(/[a-z]/) && passwd.match(/[A-Z]/) && passwd.match(/\d/) && passwd.match(/[!,@#$%^&*?_~]/))
{
intScore = (intScore+2)
}
Scriptaculous version
I used this script to create a dynamic version with scriptaculous, click here to see the (nice :-) ) result.
download
From here
Another taker
And another
Source code license?
Re: Source code license
Java version with minor regex fixes
Jim
Cool
new regex for combo
if (passwd.match(/([a-zA-Z])/) && passwd.match(/([0-9])/)) { // both letters and numbersyou might have already changed that... or please let me know if that doesn't work. thanks again!
---
zack
zacksmithdesign.com
Thanks!
Excellent application that was easy to integrate!
https://secure.quty.com/q,d/Register?cmd=user_country&QDD=global Thanks for a great app Steve! -Dick Ervasti
Cofounder / CTO
Quty Global Auction Network
Thanks
CSS meter Version
Thanks