134
+ − 1
/*
+ − 2
* Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
+ − 3
* Copyright (C) 2006-2007 Dan Fuhry
+ − 4
* pwstrength - Password evaluation and strength testing algorithm
+ − 5
*
+ − 6
* This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
+ − 7
* as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+ − 8
*
+ − 9
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ − 10
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ − 11
*/
+ − 12
+ − 13
function password_score_len(password)
+ − 14
{
+ − 15
if ( typeof(password) != "string" )
+ − 16
{
+ − 17
return -10;
+ − 18
}
+ − 19
var len = password.length;
+ − 20
var score = len - 7;
+ − 21
return score;
+ − 22
}
+ − 23
+ − 24
function password_score(password)
+ − 25
{
+ − 26
if ( typeof(password) != "string" )
+ − 27
{
+ − 28
return -10;
+ − 29
}
+ − 30
var score = 0;
+ − 31
var debug = [];
+ − 32
// length check
+ − 33
var lenscore = password_score_len(password);
+ − 34
+ − 35
debug.push(''+lenscore+' points for length');
+ − 36
+ − 37
score += lenscore;
+ − 38
+ − 39
var has_upper_lower = false;
+ − 40
var has_symbols = false;
+ − 41
var has_numbers = false;
+ − 42
+ − 43
// contains uppercase and lowercase
+ − 44
if ( password.match(/[A-z]+/) && password.toLowerCase() != password )
+ − 45
{
+ − 46
score += 1;
+ − 47
has_upper_lower = true;
+ − 48
debug.push('1 point for having uppercase and lowercase');
+ − 49
}
+ − 50
+ − 51
// contains symbols
+ − 52
if ( password.match(/[^A-z0-9]+/) )
+ − 53
{
+ − 54
score += 1;
+ − 55
has_symbols = true;
+ − 56
debug.push('1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)');
+ − 57
}
+ − 58
+ − 59
// contains numbers
+ − 60
if ( password.match(/[0-9]+/) )
+ − 61
{
+ − 62
score += 1;
+ − 63
has_numbers = true;
+ − 64
debug.push('1 point for having numbers');
+ − 65
}
+ − 66
+ − 67
if ( has_upper_lower && has_symbols && has_numbers && password.length >= 9 )
+ − 68
{
+ − 69
// if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points
+ − 70
score += 4;
+ − 71
debug.push('4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters');
+ − 72
}
+ − 73
else if ( has_upper_lower && has_symbols && has_numbers && password.length >= 6 )
+ − 74
{
+ − 75
// still give some points for passing complexity check
+ − 76
score += 2;
+ − 77
debug.push('2 points for having uppercase and lowercase, numbers, and nonalphanumeric');
+ − 78
}
+ − 79
else if(( ( has_upper_lower && has_symbols ) ||
+ − 80
( has_upper_lower && has_numbers ) ||
+ − 81
( has_symbols && has_numbers ) ) && password.length >= 6 )
+ − 82
{
+ − 83
// if 2 of the three main complexity checks passed, add a point
+ − 84
score += 1;
+ − 85
debug.push('1 point for having 2 of 3 complexity checks');
+ − 86
}
+ − 87
else if ( ( !has_upper_lower && !has_numbers && has_symbols ) ||
+ − 88
( !has_upper_lower && !has_symbols && has_numbers ) ||
+ − 89
( !has_numbers && !has_symbols && has_upper_lower ) )
+ − 90
{
+ − 91
score += -2;
+ − 92
debug.push('-2 points for only meeting 1 complexity check');
+ − 93
}
+ − 94
else if ( password.match(/^[0-9]*?([a-z]+)[0-9]?$/) )
+ − 95
{
+ − 96
// password is something like magnum1 which will be cracked in seconds
+ − 97
score += -4;
+ − 98
debug.push('-4 points for being of the form [number][word][number], which is easily cracked');
+ − 99
}
+ − 100
else if ( !has_upper_lower && !has_numbers && !has_symbols )
+ − 101
{
+ − 102
// this is if somehow the user inputs a password that doesn't match the rule above, but still doesn't contain upper and lowercase, numbers, or symbols
+ − 103
debug.push('-3 points for not meeting any complexity checks');
+ − 104
score += -3;
+ − 105
}
+ − 106
+ − 107
//
+ − 108
// Repetition
+ − 109
// Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points
+ − 110
// None of the positive ones kick in unless the length is at least 8
+ − 111
//
+ − 112
+ − 113
if ( password.match(/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/) )
+ − 114
{
+ − 115
debug.push('-2 points for having more than 4 letters of the same case in a row');
+ − 116
score += -2;
+ − 117
}
+ − 118
else if ( password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) )
+ − 119
{
+ − 120
debug.push('-1 points for having more than 3 letters of the same case in a row');
+ − 121
score += -1;
+ − 122
}
+ − 123
else if ( password.match(/[A-z]/) && !password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) && password.length >= 8 )
+ − 124
{
+ − 125
debug.push('1 point for never having more than 2 letters of the same case in a row');
+ − 126
score += 1;
+ − 127
}
+ − 128
+ − 129
if ( password.match(/[0-9][0-9][0-9][0-9]/) )
+ − 130
{
+ − 131
debug.push('-2 points for having 4 or more numbers in a row');
+ − 132
score += -2;
+ − 133
}
+ − 134
else if ( password.match(/[0-9][0-9][0-9]/) )
+ − 135
{
+ − 136
debug.push('-1 points for having 3 or more numbers in a row');
+ − 137
score += -1;
+ − 138
}
+ − 139
else if ( has_numbers && !password.match(/[0-9][0-9][0-9]/) && password.length >= 8 )
+ − 140
{
+ − 141
debug.push('1 point for never more than 2 numbers in a row');
+ − 142
score += -1;
+ − 143
}
+ − 144
+ − 145
// make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row
+ − 146
var prev_char = '';
+ − 147
var warn = false;
+ − 148
var loss = 0;
+ − 149
for ( var i = 0; i < password.length; i++ )
+ − 150
{
+ − 151
var chr = password.substr(i, 1);
+ − 152
if ( chr == prev_char && warn )
+ − 153
{
+ − 154
loss += -1;
+ − 155
}
+ − 156
else if ( chr == prev_char && !warn )
+ − 157
{
+ − 158
warn = true;
+ − 159
}
+ − 160
else if ( chr != prev_char && warn )
+ − 161
{
+ − 162
warn = false;
+ − 163
}
+ − 164
prev_char = chr;
+ − 165
}
+ − 166
if ( loss < 0 )
+ − 167
{
+ − 168
debug.push(''+loss+' points for immediate character repetition');
+ − 169
score += loss;
+ − 170
// this can bring the score below -10 sometimes
+ − 171
if ( score < -10 )
+ − 172
{
+ − 173
debug.push('Score set to -10 because it went below that floor');
+ − 174
score = -10;
+ − 175
}
+ − 176
}
+ − 177
+ − 178
var debug_txt = "<b>How this score was calculated</b>\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n\n";
+ − 179
for ( var i = 0; i < debug.length; i++ )
+ − 180
{
+ − 181
debug_txt += debug[i] + "\n";
+ − 182
}
+ − 183
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 184
// For users that really want to know why their password sucks.
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 185
// Not localized because the feature is really only used for debugging the algorithm.
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 186
if ( document.getElementById('passdebug') )
134
+ − 187
document.getElementById('passdebug').innerHTML = debug_txt;
+ − 188
+ − 189
return score;
+ − 190
}
+ − 191
+ − 192
function password_score_draw(score)
+ − 193
{
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 194
if ( !$lang )
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 195
{
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 196
// $lang isn't initted yet, this happens sometimes on the usercp/emailpassword form.
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 197
// Try to init it if we have ENANO_LANG_ID and enano_lang; if not, report an error.
586
234ddd896555
Made encryption work in form-based logon again; modified load_component() to fetch compressed versions when possible
Dan
diff
changeset
+ − 198
load_component('l10n');
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 199
if ( typeof(enano_lang) == 'object' && typeof(ENANO_LANG_ID) == 'number' )
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 200
{
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 201
language_onload();
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 202
}
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 203
else
460
+ − 204
{
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 205
return {
460
+ − 206
'color' : '#000000',
+ − 207
'fgcolor' : '#666666',
+ − 208
'str' : 'Language init failed'
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 209
};
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 210
}
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 211
}
134
+ − 212
// some colors are from the Gmail sign-up form
+ − 213
if ( score >= 10 )
+ − 214
{
504
bc8e0e9ee01d
Added support for embedding language data into plugins; updated all version numbers on plugin files
Dan
diff
changeset
+ − 215
var color = '#010101';
134
+ − 216
var fgcolor = '#666666';
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 217
var str = $lang.get('usercp_pwstrength_score_verystrong', { score: score });
134
+ − 218
}
+ − 219
else if ( score > 3 )
+ − 220
{
+ − 221
var color = '#008000';
+ − 222
var fgcolor = '#004000';
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 223
var str = $lang.get('usercp_pwstrength_score_strong', { score: score });
134
+ − 224
}
+ − 225
else if ( score >= 1 )
+ − 226
{
+ − 227
var color = '#6699cc';
+ − 228
var fgcolor = '#4477aa';
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 229
var str = $lang.get('usercp_pwstrength_score_good', { score: score });
134
+ − 230
}
+ − 231
else if ( score >= -3 )
+ − 232
{
+ − 233
var color = '#f5ac00';
+ − 234
var fgcolor = '#ffcc33';
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 235
var str = $lang.get('usercp_pwstrength_score_fair', { score: score });
134
+ − 236
}
+ − 237
else
+ − 238
{
+ − 239
var color = '#aa0033';
+ − 240
var fgcolor = '#FF6060';
362
02d315d1cc58
Started localization on User CP. Localized pagination, password strength, and various other small widgets. Fixed bug in path manager causing return of fullpage from get_page_id_from_url() even when namespace is Special.
Dan
diff
changeset
+ − 241
var str = $lang.get('usercp_pwstrength_score_weak', { score: score });
134
+ − 242
}
504
bc8e0e9ee01d
Added support for embedding language data into plugins; updated all version numbers on plugin files
Dan
diff
changeset
+ − 243
var ret = {
134
+ − 244
color: color,
+ − 245
fgcolor: fgcolor,
+ − 246
str: str
+ − 247
};
504
bc8e0e9ee01d
Added support for embedding language data into plugins; updated all version numbers on plugin files
Dan
diff
changeset
+ − 248
return ret;
134
+ − 249
}
+ − 250
+ − 251
function password_score_field(field)
+ − 252
{
+ − 253
var indicator = false;
+ − 254
if ( field.nextSibling )
+ − 255
{
+ − 256
if ( field.nextSibling.className == 'password-checker' )
+ − 257
{
+ − 258
indicator = field.nextSibling;
+ − 259
}
+ − 260
}
+ − 261
if ( !indicator )
+ − 262
{
+ − 263
var indicator = document.createElement('span');
+ − 264
indicator.className = 'password-checker';
+ − 265
if ( field.nextSibling )
+ − 266
{
+ − 267
field.parentNode.insertBefore(indicator, field.nextSibling);
+ − 268
}
+ − 269
else
+ − 270
{
+ − 271
field.parentNode.appendChild(indicator);
+ − 272
}
+ − 273
}
+ − 274
var score = password_score(field.value);
+ − 275
var data = password_score_draw(score);
+ − 276
indicator.style.color = data.color;
+ − 277
indicator.style.fontWeight = 'bold';
+ − 278
indicator.innerHTML = ' ' + data.str;
+ − 279
+ − 280
if ( document.getElementById('pwmeter') )
+ − 281
{
+ − 282
var div = document.getElementById('pwmeter');
+ − 283
div.style.width = '250px';
+ − 284
score += 10;
+ − 285
if ( score > 25 )
+ − 286
score = 25;
+ − 287
div.style.backgroundColor = data.color;
+ − 288
var width = Math.round( score * (250 / 25) );
+ − 289
div.innerHTML = '<div style="width: '+width+'px; background-color: '+data.fgcolor+'; height: 8px;"></div>';
+ − 290
}
+ − 291
}
+ − 292