Link to home
Start Free TrialLog in
Avatar of Rich Rumble
Rich RumbleFlag for United States of America

asked on

javascript to enforce password complexity

I'm doing a demonstration for Security Awareness Training, and I want a web-form that users can visit on the internal intranet and see if their password will be rejected or weak.
I found a js-fiddle that has some good regex, but I'd like to expand that to also include a routine that looks for words as well.
http://jsfiddle.net/aleem/KE3RB/8/ <--This is pretty good as far as the main complexity of our companies settings, 99% of the passwords I put in, and that match "STRONG" are passwords we can use. I'd like to have one final check, look for the top 500 most common passwords as well.
https://wiki.skullsecurity.org/images/c/ca/500-worst-passwords.txt
I'd like the passwords in an csv or array inside the JS rather than calling the list from an external site. I clearly don't know enough about JS to mange this myself. Can anyone help me with adding in another check for that list of words?
Thanks!
-rich
Avatar of hielo
hielo
Flag of Wallis and Futuna image

try:
$('#pass').keyup(function(e) {
     var strongRegex = new RegExp("^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$", "g");
     var mediumRegex = new RegExp("^(?=.{7,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$", "g");
     var enoughRegex = new RegExp("(?=.{6,}).*", "g");
     
     var val = $(this).val();

		 // provide list of words
		 var words='123456, password, 12345678, 1234, 12345, dragon, qwerty, 696969, mustang, letmein, baseball, master, michael, football, shadow, monkey, abc123, pass, fuckme, 6969, jordan, harley, ranger, iwantu, jennifer, hunter, fuck, 2000, test, batman, trustno1, thomas, tigger, robert, access, love, buster, 1234567, soccer, hockey, killer, george, sexy, andrew, charlie, superman, asshole, fuckyou, dallas, jessica, panties, pepper, 1111, austin, william, daniel, golfer, summer, heather, hammer, yankees, joshua, maggie, biteme, enter, ashley, thunder, cowboy, silver, richard, fucker, orange, merlin, michelle, corvette, bigdog, cheese, matthew, 121212, patrick, martin, freedom, ginger, blowjob, nicole, sparky, yellow, camaro, secret, dick, falcon, taylor, 111111, 131313, 123123, bitch, hello, scooter, please, porsche, guitar, chelsea, black, diamond, nascar, jackson, cameron, 654321, computer, amanda, wizard, xxxxxxxx, money, phoenix, mickey, bailey, knight, iceman, tigers, purple, andrea, horny, dakota, aaaaaa, player, sunshine, morgan, starwars, boomer, cowboys, edward, charles, girls, booboo, coffee, xxxxxx, bulldog, ncc1701, rabbit, peanut, john, johnny, gandalf, spanky, winter, brandy, compaq, carlos, tennis, james, mike, brandon, fender, anthony, blowme, ferrari, cookie, chicken, maverick, chicago, joseph, diablo, sexsex, hardcore, 666666, willie, welcome, chris, panther, yamaha, justin, banana, driver, marine, angels, fishing, david, maddog, hooters, wilson, butthead, dennis, fucking, captain, bigdick, chester, smokey, xavier, steven, viking, snoopy, blue, eagles, winner, samantha, house, miller, flower, jack, firebird, butter, united, turtle, steelers, tiffany, zxcvbn, tomcat, golf, bond007, bear, tiger, doctor, gateway, gators, angel, junior, thx1138, porno, badboy, debbie, spider, melissa, booger, 1212, flyers, fish, porn, matrix, teens, scooby, jason, walter, cumshot, boston, braves, yankee, lover, barney, victor, tucker, princess, mercedes, 5150, doggie, zzzzzz, gunner, horney, bubba, 2112, fred, johnson, xxxxx, tits, member, boobs, donald, bigdaddy, bronco, penis, voyager, rangers, birdie, trouble, white, topgun, bigtits, bitches, green, super, qazwsx, magic, lakers, rachel, slayer, scott, 2222, asdf, video, london, 7777, marlboro, srinivas, internet, action, carter, jasper, monster, teresa, jeremy, 11111111, bill, crystal, peter, pussies, cock, beer, rocket, theman, oliver, prince, beach, amateur, 7777777, muffin, redsox, star, pussy, testing, shannon, murphy, frank, hannah, dave, eagle1, 11111, mother, nathan, raiders, steve, forever, angela, viper, ou812, jake, lovers, suckit, gregory, buddy, whatever, young, nicholas, lucky, helpme, jackie, monica, midnight, college, baby, cunt, brian, mark, startrek, sierra, leather, 232323, 4444, beavis, bigcock, happy, sophie, ladies, naughty, giants, booty, blonde, fucked, golden, 0, fire, sandra, pookie, packers, einstein, dolphins, 0, chevy, winston, warrior, sammy, slut, 8675309, zxcvbnm, nipples, power, victoria, asdfgh, vagina, toyota, travis, hotdog, paris, rock, xxxx, extreme, redskins, erotic, dirty, ford, freddy, arsenal, access14, wolf, nipple, iloveyou, alex, florida, eric, legend, movie, success, rosebud, jaguar, great, cool, cooper, 1313, scorpio, mountain, madison, 987654, brazil, lauren, japan, naked, squirt, stars, apple, alexis, aaaa, bonnie, peaches, jasmine, kevin, matt, qwertyui, danielle, beaver, 4321, 4128, runner, swimming, dolphin, gordon, casper, stupid, shit, saturn, gemini, apples, august, 3333, canada, blazer, cumming, hunting, kitty, rainbow, 112233, arthur, cream, calvin, shaved, surfer, samson, kelly, paul, mine, king, racing, 5555, eagle, hentai, newyork, little, redwings, smith, sticky, cocacola, animal, broncos, private, skippy, marvin, blondes, enjoy, girl, apollo, parker, qwert, time, sydney, women, voodoo, magnum, juice, abgrtyu, 777777, dreams, maxwell, music, rush2112, russia, scorpion, rebecca, tester, mistress, phantom, billy, 6666, albert';

		 // break list of words
     words = words.split(/\W+/);
     
     // see if word exists in lis
     for( var i=0; i<words.length; ++i)
     {
     		if(words[i]===val)
      	{
        		$('#passstrength').className = 'error';
            $('#passstrength').html('Common Word!');
            return true;
				}      
     }
     
     if (false == enoughRegex.test(val)) {
             $('#passstrength').html('More Characters');
     } else if (strongRegex.test(val)) {
             $('#passstrength').className = 'ok';
             $('#passstrength').html('Strong!');
     } else if (mediumRegex.test( val )) {
             $('#passstrength').className = 'alert';
             $('#passstrength').html('Medium!');
     } else {
             $('#passstrength').className = 'error';
             $('#passstrength').html('Weak!');
     }
     return true;
});

Open in new window

Avatar of Rich Rumble

ASKER

Super close, I'd like it to be more "grep like", should it find even one of those words, rate it as weak-common-word.
I tried these, both I'd like to be disqualified because they contain "password" in them.
password1234A! (says strong, but I'd like it as weak)
Password1234! (says strong, but I'd like it as weak)

Capital "P" in password threw it off from the start, should probably be case insensitive.
Others I'd like it to disqualify because of a word (weak-common-word)
123PaSSword!
!!passWORD2
Is that possible? Thanks again, this should be exactly what I need when it can disqualify passes like those!
-rich
Complex, hard-to-remember passwords are still easily cracked and are a hateful anti-pattern.  Please don't do that to your clients.

Consider using a pass-phrase instead of a password.  Just don't use Correct Horse Battery staple.
https://xkcd.com/936/

You might try this service (you must enable cookies for it to work).
http://correcthorsebatterystaple.net/

For a better understanding of the issues, please search this article for An Afterword: About Storing Passwords.
https://www.experts-exchange.com/articles/2391/PHP-Client-Registration-Login-Logout-and-Easy-Access-Control.html
Oh I know Ray, I'm the king of passwords :) but we have to do baby steps for the users, this is one such step in their education.
https://www.experts-exchange.com/articles/12386/How-secure-are-passwords.html <--me
-rich
ASKER CERTIFIED SOLUTION
Avatar of hielo
hielo
Flag of Wallis and Futuna image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
That's excellent! I think this is a good step in the right direction for many of the users, thanks for doing this!
I forked the JSFiddle, and changed the ***** box to a normal input for testing
http://jsfiddle.net/xr2xyshs/1/ (change input type hide the input)
-rich
Very good work, thanks!
ty!
greetings richrumble, , your last comment about -
    "password1234A! (says strong, but I'd like it as weak)"

would be terribly difficult to implement because there are TOO MANY character variations possible, and as far as the "security" considerations for this testing. . . .

My opinion is -
I can NOT see any benefit to it, the dictionary attacks (using common 'bad passwords'), can not use "combination" test attempts like -
    password2
or
    password1234A!

because a phrase like "password" in the entry characters for a site , does not lower security much, if any, when there are other characters included with that.

Just because a common phrase like "baseball" is a risk in dictionary attacks, when that's all of the characters in the pass phrase, but if just one other character is in the phrase, then the dictionary attack is not effective, since adding just a single character "baseball9" will require a multiplier of 92 tests (the english keyboard has about 92 possible entries), so this will convert it to a modified Bruteforce attack. And if the characters can also be placed before as "9baseball" then the dictionary words as part of string, are no longer a disadvantage in security? ? ?

If you are concerned with javascript detection of security ratings of password entries, I can not see you using such a simplistic test as your -
strongRegex = new RegExp("^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$", "g");

 mediumRegex = new RegExp("^(?=.{7,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$", "g");

enoughRegex = new RegExp("(?=.{6,}).*", "g");

Open in new window


below is some code you might consider that uses a better calculation formulae for a relative security rating by javascript -

first the HTML -
<style>
#passCon {
position:relative;
width: 20em;
border: 2px dotted #b0d;
padding:8px;
}

#warn {
position:absolute;
display: none;
top:5px;
left:5px;
background: #dc0;
padding:8px;
border:2px solid #33b;
z-index:2;
}

#info1 {
text-align:center;
text-decoration: italic;
}

#indic {
width:300px;
height:23px;
border:3px solid #a22;
margin: 4px 0;
}

#prog {
width:2px;
height:23px;
background:#d41010;
}
</style>
<div id="passCon">
<div id="warn">You can ass A Calpital letter</div>
<div id="info1">Your password must be at least 8 characters long,<br />and no more than 20 characters in length</div>
Password: <input id="passIn" type="password" name="name" size="20" maxlength="20" value="" onkeyup="checkPass(this.value);" /> - <span id="chk" style="background:red;">&nbsp;less than 8&nbsp;</span><br />
<div id="indic"><div id="prog"></div></div>
</div>

Open in new window


next the javascript -
<script>

function id2ob(elmt1){return document.getElementById(elmt1);}

var domPs=id2ob("prog"), domC=id2ob("chk"), warn=id2ob("warn"), dok=0, per=0;

function rePlus(passw){
var bitM = [0,0], rank = [0,2,3,3], reg1 = passw.match(/[a-z]/g);
reg1=(reg1 != null)?reg1.length:0;
if (reg1!=0) ++bitM[1];
bitM[0]=rank[Math.min(reg1,2)];
reg1=passw.match(/[A-Z]/g);
reg1=(reg1!=null)?reg1.length:0;
if(reg1!=0)bitM[1]+=2;
bitM[0]+=rank[Math.min(reg1,2)];
reg1=passw.match(/[0-9]/g);
reg1=(reg1!=null)?reg1.length:0;
if(reg1!=0)bitM[1]+=4;
bitM[0]+=rank[Math.min(reg1,2)];
reg1=passw.match(/[\x20-\x2f\x5b-\x60\x7b-\x7e]/g);
reg1=(reg1!=null)?reg1.length:0;
if(reg1!=0)bitM[1]+=8;
bitM[0]+=rank[Math.min(reg1,2)];
return bitM;}

function checkPass(passw) {
var passAry=["password","12345678","baseball","prettygirl","maverick",
  "football","trustno1","beautiful","xxxxxxxx","qwertyui","starwars",
  "hardcore","funnygirl","butthead","steelers","supergirl"];
if(passAry.indexOf(passw)>-1) {
  warn.innerHTML = 'This password is NOT allowed - "'+passw+'"';
  warn.style.display = "block";
  id2ob("passIn").value="";
  domPs.innerHTML="";
  domPs.style.width="1%";
  setTimeout(function(){ warn.style.display = "none"; }, 3500);
  return;
  } 
var perc = rePlus(passw), spread=perc[1];
perc=(passw.length+(perc[0]*0.65))/16.5;
perc=Math.min(Math.round(perc * 98),100);
domPs.style.width=perc+"%";
if(dok==1& passw.length<8){
  domC.innerHTML="&nbsp;less than 8&nbsp;";
  domC.style.backgroundColor='red';
  dok=0;
  } 
if(passw.length>7){
  domC.style.backgroundColor="#2fe868";
  domC.innerHTML="&nbsp;length OK";
  dok=1;
  }// perc>73& 
if(passw.length==7&perc<73&spread<15){
  passw="You might add ";
  if((spread&1)==0)passw+="a small letter, ";
  if((spread&2)==0)passw+="a Capital, ";
  if((spread&4)==0)passw+="a number, ";
  if((spread&8)==0)passw+="puntuation ";
  warn.innerHTML = passw;
  warn.style.display = "block";
  //alert(passw);
  } else if(passw.length>7 || passw.length<7)warn.style.display="none";

if(perc<25){domPs.style.backgroundColor="#d41010";domPs.innerHTML="useless";return;} 
if(perc>95){domPs.style.backgroundColor="#38ff70";domPs.innerHTML="STRONG security";return;}
if(perc>83){domPs.style.backgroundColor="#98eaa1";domPs.innerHTML="medium-strong security";return;}
if(perc>73){domPs.style.backgroundColor="#bfcf91";domPs.innerHTML="medium security";return;}
if(perc>49){domPs.style.backgroundColor="efb861";domPs.innerHTML="weak";return;}
if(perc>25){domPs.style.backgroundColor="#ff7600";domPs.innerHTML="very weak";return;}
}
</script>

Open in new window

I appreciate the feedback and thought you put into the response. This Q certainly isn't meant to be an exhaustive and complete "strength meter". I am trying your code out as well right now.

Drop box actually publishes a framework (in several lang's) that is very robust I could use, and we have our developers learning from that code: https://github.com/dropbox/zxcvbn but this Q is a small session for a non-techie crowd and this simple JS get's the message across very well I think.
-rich