-
16 Jul 2007 5:41 AM #1
[Beta] Ext.ux.SecurePass
[Beta] Ext.ux.SecurePass
Hello,
First, thanks to the team and specifically Jack S to this very great Extjs.
I'm currently writing my first extension, that add a meter to evaluate the strength of an input password. It is based on the one of hotmail. Following the code needed:
scripts/secure-pass.js:
styles/secure-pass.css:Code:Ext.namespace('Ext.ux'); Ext.ux.SecurePass = function(config) { Ext.ux.SecurePass.superclass.constructor.call(this, config); } Ext.extend(Ext.ux.SecurePass, Ext.form.TextField, { /** * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to * { * PwdEmpty: "Please type a password, and then retype it to confirm.", * PwdDifRPwd: "The new password and the confirmation password don't match. Please type the same password in both boxes.", * PwdShort: "Your password must be at least 6 characters long. Please type a different password.", * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.", * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.", * IDInPwd: "Your password can't include the part of your ID. Please type a different password.", * FNInPwd: "Your password can't contain your first name. Please type a different password.", * LNInPwd: "Your password can't contain your last name. Please type a different password." * }) */ // private errors : { PwdEmpty: "Please type a password, and then retype it to confirm.", PwdDifRPwd: "The new password and the confirmation password don't match. Please type the same password in both boxes.", PwdShort: "Your password must be at least 6 characters long. Please type a different password.", PwdLong: "Your password can't contain more than 16 characters. Please type a different password.", PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.", IDInPwd: "Your password can't include the part of your ID. Please type a different password.", FNInPwd: "Your password can't contain your first name. Please type a different password.", LNInPwd: "Your password can't contain your last name. Please type a different password." }, /** * @cfg {String/Object} Label for the strength meter (defaults to * 'Password strength:') */ // private meterLabel : 'Password strength:', /** * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to * ['Weak', 'Medium', 'Strong']) */ // private pwdStrengths : ['Weak', 'Medium', 'Strong'], // private strength : 0, // private _lastPwd : null, // private kCapitalLetter : 0, kSmallLetter : 1, kDigit : 2, kPunctuation : 3, // private initEvents : function(){ Ext.ux.SecurePass.superclass.initEvents.call(this); this.el.on('keyup', this.checkStrength, this, {buffer:50}); }, // private onRender : function(ct, position){ Ext.ux.SecurePass.superclass.onRender.call(this, ct, position); this.wrap = this.el.wrap({cls: "x-form-field-wrap"}); this.trigger = this.wrap.createChild({tag: "div", cls: "StrengthMeter "+this.triggerClass}); if(this.meterLabel != ''){ this.trigger.createChild({tag: "label", html: this.meterLabel}); } this.trigger.createChild({tag: "div", cls: "PwdMeterBase", html: '<div class="PwdBack"><div class="PwdMeter" id="PwdMeter"></div></div>'}); if(this.hideTrigger){ this.trigger.setDisplayed(false); } this.setSize(this.width||'', this.height||''); }, // private onDestroy : function(){ if(this.trigger){ this.trigger.removeAllListeners(); this.trigger.remove(); } if(this.wrap){ this.wrap.remove(); } Ext.form.TriggerField.superclass.onDestroy.call(this); }, // private checkStrength : function(){ var pwd = this.el.getValue(); if (pwd == this._lastPwd) { return; } var strength; if (this.ClientSideStrongPassword(pwd)) { strength = 3; } else if(this.ClientSideMediumPassword(pwd)) { strength = 2; } else if(this.ClientSideWeakPassword(pwd)) { strength = 1; } else { strength = 0; } document.getElementById('PwdMeter').style.width = 82 * strength +'px'; if(this.pwdStrengths != null && strength > 0) { document.getElementById('PwdMeter').innerHTML = ' '+ this.pwdStrengths[strength - 1]; } else { document.getElementById('PwdMeter').innerHTML = ''; } this._lastPwd = pwd; }, // private validateValue : function(value){ if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){ return false; } if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid return true; } if(value.length == 0) { this.markInvalid(this.errors.PwdEmpty); return false; } if("[\x21-\x7e]*".match(value)) { this.markInvalid(this.errors.PwdBadChar); return false; } if(value.length < 6) { this.markInvalid(this.errors.PwdShort); return false; } if(value.length > 16) { this.markInvalid(this.errors.PwdLong); return false; } /*if(value.length > 0 && this.iRPwd != 'undefined' && Ext.get(this.iRPwd) != null && value != Ext.get(this.iRPwd).getValue()) { this.markInvalid(this.errors.PwdDifRPwd); return false; }*/ return true; }, // private CharacterSetChecks : function(type){ this.type = type; this.fResult = false; }, // private isctype : function(character, type){ switch (type) { //why needed break after return in js ? very odd bug case this.kCapitalLetter: if (character >= 'A' && character <= 'Z') { return true; } break; case this.kSmallLetter: if (character >= 'a' && character <= 'z') { return true; } break; case this.kDigit: if (character >= '0' && character <= '9') { return true; } break; case this.kPunctuation: if ("!@#$%^&*()_+-='\";:[{]}|.>,</?`~".indexOf(character) >= 0) { return true; } break; default: return false; } }, // private IsLongEnough : function(pwd, size){ return !(pwd == null || isNaN(size) || pwd.length < size); }, // private SpansEnoughCharacterSets : function(word, nb){ if (!this.IsLongEnough(word, nb)) { return false; } var characterSetChecks = new Array( new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter), new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)); for (var index = 0; index < word.length; ++index) { for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) { if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) { characterSetChecks[nCharSet].fResult = true; break; } } } var nCharSets = 0; for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) { if (characterSetChecks[nCharSet].fResult) { ++nCharSets; } } if (nCharSets < nb) { return false; } return true; }, // private ClientSideStrongPassword : function(pwd){ return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3); }, // private ClientSideMediumPassword : function(pwd){ return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2); }, // private ClientSideWeakPassword : function(pwd){ return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0); } })
and a sample html:Code:@charset "utf-8"; .PwdMeterBase{ margin-bottom:10px; width:248px; } .PwdBack{ height:9px; background-image:url(../images/password_meter_grey.gif); background-position:center center; background-repeat: no-repeat; } .PwdMeter{ width:0; height:9px; background-image:url(../images/password_meter.gif); background-position:center center; background-repeat: no-repeat; font-size:9px; }
[html]<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Secure Pass</title>
<script type="text/javascript" src="library/yui-utilities.js"></script>
<script type="text/javascript" src="library/ext-yui-adapter.js"></script>
<script type="text/javascript" src="library/ext-all.js"></script>
<script type="text/javascript" src="library/ext-lang-fr_CA.js"></script>
<script type="text/javascript" src="scripts/secure-pass.js"></script>
<script type="text/javascript">
Ext.onReady(function() {
sPwd = new Ext.ux.SecurePass({
iRPwd : 'iRetypePwd',
pwdStrengths : ['Faible', 'Moyen', 'Fort'],
errors : {
PwdEmpty: 'Tapez un mot de passe, puis retapez-le pour confirmation.',
PwdDifRPwd: 'Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas. Tapez le m
-
16 Jul 2007 6:01 AM #2
-
16 Jul 2007 6:30 AM #3
Yeah looks great, cannot wait to try this out
I'm part of the Ext Community
Maintaining: Translations and some Examples
Developing on: ExtJS Python Builder / Gozerbot
Places: Ido.nl.eu.org / My ExtSamples / Trbs on Wiki / IRC
-
17 Jul 2007 1:47 AM #4
[1.0] Ext.ux.form.SecurePass
[1.0] Ext.ux.form.SecurePass
Hello,
Please find here the final version:
Strong passwords contain 8-16 characters, do not include other fields, and combine three of these character types: uppercase letters, lowercase letters, numbers, or symbols.
Medium passwords contain 7 characters, do not other fields, and combine two of the character types.
Weak passwords contain 6 characters minimum, do not include other fields.
scripts/secure-pass.js
styles/secure-pass.cssCode:Ext.namespace('Ext.ux.form'); Ext.ux.form.SecurePass = function(config) { Ext.ux.form.SecurePass.superclass.constructor.call(this, config); } Ext.extend(Ext.ux.form.SecurePass, Ext.form.TextField, { /** * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to * { * PwdEmpty: "Please type a password, and then retype it to confirm.", * PwdShort: "Your password must be at least 6 characters long. Please type a different password.", * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.", * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.", * IDInPwd: "Your password can't include the part of your ID. Please type a different password.", * FNInPwd: "Your password can't contain your first name. Please type a different password.", * LNInPwd: "Your password can't contain your last name. Please type a different password." * }) */ // private errors : { PwdEmpty: "Please type a password, and then retype it to confirm.", PwdShort: "Your password must be at least 6 characters long. Please type a different password.", PwdLong: "Your password can't contain more than 16 characters. Please type a different password.", PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.", IDInPwd: "Your password can't include the part of your ID. Please type a different password.", FNInPwd: "Your password can't contain your first name. Please type a different password.", LNInPwd: "Your password can't contain your last name. Please type a different password." }, /** * @cfg {String/Object} Label for the strength meter (defaults to * 'Password strength:') */ // private meterLabel : 'Password strength:', /** * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to * ['Weak', 'Medium', 'Strong']) */ // private pwdStrengths : ['Weak', 'Medium', 'Strong'], /** * @cfg {String/Object} fieldsFilter A fieldsFilter spec, as [['field_name', 'error_id'], ...] */ // private fieldsFilter : null, // private strength : 0, // private _lastPwd : null, // private kCapitalLetter : 0, kSmallLetter : 1, kDigit : 2, kPunctuation : 3, // private initEvents : function(){ Ext.ux.form.SecurePass.superclass.initEvents.call(this); this.el.on('keyup', this.checkStrength, this, {buffer:50}); }, // private onRender : function(ct, position){ Ext.ux.form.SecurePass.superclass.onRender.call(this, ct, position); this.wrap = this.el.wrap({cls: "x-form-field-wrap"}); this.trigger = this.wrap.createChild({tag: "div", cls: "StrengthMeter "+this.triggerClass}); if(this.meterLabel != ''){ this.trigger.createChild({tag: "label", html: this.meterLabel}); } this.trigger.createChild({tag: "div", cls: "PwdMeterBase", html: '<div class="PwdBack"><div class="PwdMeter" id="PwdMeter"></div></div>'}); if(this.hideTrigger){ this.trigger.setDisplayed(false); } this.setSize(this.width||'', this.height||''); }, // private onDestroy : function(){ if(this.trigger){ this.trigger.removeAllListeners(); this.trigger.remove(); } if(this.wrap){ this.wrap.remove(); } Ext.form.TriggerField.superclass.onDestroy.call(this); }, // private checkStrength : function(){ var pwd = this.el.getValue(); if (pwd == this._lastPwd) { return; } var strength; if (this.ClientSideStrongPassword(pwd)) { strength = 3; } else if(this.ClientSideMediumPassword(pwd)) { strength = 2; } else if(this.ClientSideWeakPassword(pwd)) { strength = 1; } else { strength = 0; } document.getElementById('PwdMeter').style.width = 82 * strength +'px'; if(this.pwdStrengths != null && strength > 0) { document.getElementById('PwdMeter').innerHTML = ' '+ this.pwdStrengths[strength - 1]; } else { document.getElementById('PwdMeter').innerHTML = ''; } this._lastPwd = pwd; }, // private validateValue : function(value){ if (!Ext.form.TextField.superclass.validateValue.call(this, value)){ return false; } if (value.length == 0) { this.markInvalid(this.errors.PwdEmpty); return false; } if ("[\x21-\x7e]*".match(value)) { this.markInvalid(this.errors.PwdBadChar); return false; } if (value.length < 6) { this.markInvalid(this.errors.PwdShort); return false; } if (value.length > 16) { this.markInvalid(this.errors.PwdLong); return false; } for (var index = 0; index < this.fieldsFilter.length; ++index) { filter = document.getElementById(this.fieldsFilter[index][0]).value; if (filter != '') { re = new RegExp(filter); if (re.test(value)) { this.markInvalid(eval('this.errors.'+ this.fieldsFilter[index][1])); return false; } } } return true; }, // private CharacterSetChecks : function(type){ this.type = type; this.fResult = false; }, // private isctype : function(character, type){ switch (type) { //why needed break after return in js ? very odd bug case this.kCapitalLetter: if (character >= 'A' && character <= 'Z') { return true; } break; case this.kSmallLetter: if (character >= 'a' && character <= 'z') { return true; } break; case this.kDigit: if (character >= '0' && character <= '9') { return true; } break; case this.kPunctuation: if ("!@#$%^&*()_+-='\";:[{]}|.>,</?`~".indexOf(character) >= 0) { return true; } break; default: return false; } }, // private IsLongEnough : function(pwd, size){ return !(pwd == null || isNaN(size) || pwd.length < size); }, // private SpansEnoughCharacterSets : function(word, nb){ if (!this.IsLongEnough(word, nb)) { return false; } var characterSetChecks = new Array( new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter), new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)); for (var index = 0; index < word.length; ++index) { for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) { if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) { characterSetChecks[nCharSet].fResult = true; break; } } } var nCharSets = 0; for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) { if (characterSetChecks[nCharSet].fResult) { ++nCharSets; } } if (nCharSets < nb) { return false; } return true; }, // private ClientSideStrongPassword : function(pwd){ return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3); }, // private ClientSideMediumPassword : function(pwd){ return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2); }, // private ClientSideWeakPassword : function(pwd){ return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0); } })
and a html sample...Code:@charset "utf-8"; .PwdMeterBase{ margin-bottom:10px; width:248px; } .PwdBack{ height:9px; background-image:url(../images/password_meter_grey.gif); background-position:center center; background-repeat: no-repeat; } .PwdMeter{ width:0; height:9px; background-image:url(../images/password_meter.gif); background-position:center center; background-repeat: no-repeat; font-size:9px; }
Possible evolutions :HTML Code:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Secure Pass</title> <script type="text/javascript" src="library/yui-utilities.js"></script> <script type="text/javascript" src="library/ext-yui-adapter.js"></script> <script type="text/javascript" src="library/ext-all.js"></script> <script type="text/javascript" src="scripts/secure-pass.js"></script> <script type="text/javascript"> Ext.onReady(function() { sPwd = new Ext.ux.form.SecurePass({ fieldsFilter : [['lastname', 'LNInPwd']] }); sPwd.applyTo('iPwd'); }); </script> <style> @import 'styles/ext-all.css'; @import 'styles/secure-pass.css'; </style> </head> <body> <div class="x-form-element"> <label for="lastname">*Last name:</label> <input id="lastname" name="lastname" maxlength="32" type="text"> </div> <div class="x-form-element"> <label for="iPwd">*Type password:</label> <input id="iPwd" name="iPwd" maxlength="16" type="password"> </div> </body> </html>
- manage retype password
- dictionnary of common words
- custom strength functions
- custom minimum / maximum password length
Again, thanks to Ext team for their work.
Hope you find this usefull.
Enjoy !
-
17 Jul 2007 9:30 PM #5Sencha - Community Support Team
- Join Date
- Mar 2007
- Location
- Forest Grove, OR
- Posts
- 1,038
- Vote Rating
- 0
Resources on password strength
Resources on password strength
I think this is a great start. However, like pretty much everything Ext, develop this to the nth degree and combine all the right decisions from the various players in the field, the whole time, doing it overall better. With that, here's some of what's currently in the market:
http://www.certainkey.com/demos/password/
Notice that it calculates entropy and detects dictionary words.
http://www.codeandcoffee.com/2007/06...r-like-google/
An example based off Google's approach
Additionally, the checker should "canonicalize" (normalize or convert to all-letter form) the password prior to checks against the dictionary. This will catch common combinations of using 5 for s, 1 for l or i, 2 for z, 8 for b or B, etc.
Here's some good advice regarding passwords:
http://geodsoft.com/howto/password/password_advice.htmJeff Howden
Ext JS - Support Team Volunteer
jeff@extjs.com
Any and all code samples that are authored by me and posted on the Ext forums or website are hereby released into the public domain and I release anyone or entity of liability by using said code samples unless explicitly stated otherwise.
Opinions are mine and not necessarily endorsed by Ext, LLC. Please do not contact me directly for assistance unless requested by me.
-
21 Jul 2007 1:36 AM #6
Or you could do password strength checking 'in the cloud' and provide an Ext UI to the Google checker: https://www.google.com/accounts/Rate...wd=amI2Strong? which has been written about here http://www.codeproject.com/useritems...rdStrength.asp .
-
21 Jul 2007 4:22 PM #7
No offence but using a third party to determine the strength of your password? Why not just send them the keys to your house while your at it?
-
21 Jul 2007 9:34 PM #8Sencha - Community Support Team
- Join Date
- Mar 2007
- Location
- Forest Grove, OR
- Posts
- 1,038
- Vote Rating
- 0
I briefly considered that too, but I highly dislike the notion of a) sending the password over the wire, b) using an external service that has no documentation on their practice of traffic logging on that address, and c) there being literally no documentation as to how the strength checking works (ie, is it just length, is it a count of different character types, does it do dictionary checks, does it include any sort of "other field" checking to ensure things like username, first name, last name, email, etc. aren't in the password).
Just a guess, but from the following scoring a 4, I'd say their strength checking really isn't very full-featured:
https://www.google.com/accounts/Rate...strongpassword
https://www.google.com/accounts/Rate...swd=mypassword
https://www.google.com/accounts/Rate...?Passwd=iphone
https://www.google.com/accounts/Rate...d?Passwd=extjs
https://www.google.com/accounts/Rate...Passwd=jscriptJeff Howden
Ext JS - Support Team Volunteer
jeff@extjs.com
Any and all code samples that are authored by me and posted on the Ext forums or website are hereby released into the public domain and I release anyone or entity of liability by using said code samples unless explicitly stated otherwise.
Opinions are mine and not necessarily endorsed by Ext, LLC. Please do not contact me directly for assistance unless requested by me.


Reply With Quote

