Skip to content

Commit

Permalink
MDL-35332 lib: Improve security of hashed passwords
Browse files Browse the repository at this point in the history
  • Loading branch information
simoncoggins committed Feb 8, 2013
1 parent 6319737 commit ec2d8ce
Show file tree
Hide file tree
Showing 27 changed files with 808 additions and 111 deletions.
19 changes: 15 additions & 4 deletions admin/tool/uploaduser/index.php
Expand Up @@ -618,14 +618,16 @@
// Do not mess with passwords of remote users.

} else if (!$isinternalauth) {
$existinguser->password = 'not cached';
$existinguser->password = AUTH_PASSWORD_NOT_CACHED;
$upt->track('password', '-', 'normal', false);
// clean up prefs
unset_user_preference('create_password', $existinguser);
unset_user_preference('auth_forcepasswordchange', $existinguser);

} else if (!empty($user->password)) {
if ($updatepasswords) {
// Check for passwords that we want to force users to reset next
// time they log in.
$errmsg = null;
$weak = !check_password_policy($user->password, $errmsg);
if ($resetpasswords == UU_PWRESET_ALL or ($resetpasswords == UU_PWRESET_WEAK and $weak)) {
Expand All @@ -638,7 +640,12 @@
unset_user_preference('auth_forcepasswordchange', $existinguser);
}
unset_user_preference('create_password', $existinguser); // no need to create password any more
$existinguser->password = hash_internal_user_password($user->password);

// Use a low cost factor when generating bcrypt hash otherwise
// hashing would be slow when uploading lots of users. Hashes
// will be automatically updated to a higher cost factor the first
// time the user logs in.
$existinguser->password = hash_internal_user_password($user->password, true);
$upt->track('password', $user->password, 'normal', false);
} else {
// do not print password when not changed
Expand Down Expand Up @@ -771,10 +778,14 @@
}
$forcechangepassword = true;
}
$user->password = hash_internal_user_password($user->password);
// Use a low cost factor when generating bcrypt hash otherwise
// hashing would be slow when uploading lots of users. Hashes
// will be automatically updated to a higher cost factor the first
// time the user logs in.
$user->password = hash_internal_user_password($user->password, true);
}
} else {
$user->password = 'not cached';
$user->password = AUTH_PASSWORD_NOT_CACHED;
$upt->track('password', '-', 'normal', false);
}

Expand Down
3 changes: 3 additions & 0 deletions auth/db/auth.php
Expand Up @@ -221,6 +221,9 @@ function user_update_password($user, $newpassword) {

if ($this->is_internal()) {
$puser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST);
// This will also update the stored hash to the latest algorithm
// if the existing hash is using an out-of-date algorithm (or the
// legacy md5 algorithm).
if (update_internal_user_password($puser, $newpassword)) {
$user->password = $puser->password;
return true;
Expand Down
3 changes: 3 additions & 0 deletions auth/email/auth.php
Expand Up @@ -59,6 +59,9 @@ function user_login ($username, $password) {
*/
function user_update_password($user, $newpassword) {
$user = get_complete_user_data('id', $user->id);
// This will also update the stored hash to the latest algorithm
// if the existing hash is using an out-of-date algorithm (or the
// legacy md5 algorithm).
return update_internal_user_password($user, $newpassword);
}

Expand Down
3 changes: 3 additions & 0 deletions auth/ldap/auth.php
Expand Up @@ -529,6 +529,9 @@ function user_signup($user, $notify=true) {
profile_save_data($user);

$this->update_user_record($user->username);
// This will also update the stored hash to the latest algorithm
// if the existing hash is using an out-of-date algorithm (or the
// legacy md5 algorithm).
update_internal_user_password($user, $plainslashedpassword);

$user = $DB->get_record('user', array('id'=>$user->id));
Expand Down
3 changes: 3 additions & 0 deletions auth/manual/auth.php
Expand Up @@ -82,6 +82,9 @@ function user_login($username, $password) {
*/
function user_update_password($user, $newpassword) {
$user = get_complete_user_data('id', $user->id);
// This will also update the stored hash to the latest algorithm
// if the existing hash is using an out-of-date algorithm (or the
// legacy md5 algorithm).
return update_internal_user_password($user, $newpassword);
}

Expand Down
3 changes: 3 additions & 0 deletions auth/none/auth.php
Expand Up @@ -59,6 +59,9 @@ function user_login ($username, $password) {
*/
function user_update_password($user, $newpassword) {
$user = get_complete_user_data('id', $user->id);
// This will also update the stored hash to the latest algorithm
// if the existing hash is using an out-of-date algorithm (or the
// legacy md5 algorithm).
return update_internal_user_password($user, $newpassword);
}

Expand Down
3 changes: 3 additions & 0 deletions auth/webservice/auth.php
Expand Up @@ -85,6 +85,9 @@ function user_login_webservice($username, $password) {
*/
function user_update_password($user, $newpassword) {
$user = get_complete_user_data('id', $user->id);
// This will also update the stored hash to the latest algorithm
// if the existing hash is using an out-of-date algorithm (or the
// legacy md5 algorithm).
return update_internal_user_password($user, $newpassword);
}

Expand Down
2 changes: 1 addition & 1 deletion backup/util/dbops/restore_dbops.class.php
Expand Up @@ -1052,7 +1052,7 @@ public static function create_included_users($basepath, $restoreid, $userid) {

// Most external plugins do not store passwords locally
if (!empty($userauth->preventpassindb)) {
$user->password = 'not cached';
$user->password = AUTH_PASSWORD_NOT_CACHED;

// If Moodle is responsible for storing/validating pwd and reset functionality is available, mark
} else if ($userauth->isinternal and $userauth->canresetpwd) {
Expand Down
60 changes: 31 additions & 29 deletions config-dist.php
Expand Up @@ -63,28 +63,7 @@


//=========================================================================
// 2. SECRET PASSWORD SALT
//=========================================================================
// User password salt is very important security feature, it is created
// automatically in installer, you have to uncomment and modify value
// on the next line if you are creating config.php manually.
//
// $CFG->passwordsaltmain = 'a_very_long_random_string_of_characters#@6&*1';
//
// After changing the main salt you have to copy old value into one
// of the following settings - this allows migration to the new salt
// during the next login of each user.
//
// $CFG->passwordsaltalt1 = '';
// $CFG->passwordsaltalt2 = '';
// $CFG->passwordsaltalt3 = '';
// ....
// $CFG->passwordsaltalt19 = '';
// $CFG->passwordsaltalt20 = '';


//=========================================================================
// 3. WEB SITE LOCATION
// 2. WEB SITE LOCATION
//=========================================================================
// Now you need to tell Moodle where it is located. Specify the full
// web address to where moodle has been installed. If your web site
Expand All @@ -98,7 +77,7 @@


//=========================================================================
// 4. DATA FILES LOCATION
// 3. DATA FILES LOCATION
//=========================================================================
// Now you need a place where Moodle can save uploaded files. This
// directory should be readable AND WRITEABLE by the web server user
Expand All @@ -114,7 +93,7 @@


//=========================================================================
// 5. DATA FILES PERMISSIONS
// 4. DATA FILES PERMISSIONS
//=========================================================================
// The following parameter sets the permissions of new directories
// created by Moodle within the data directory. The format is in
Expand All @@ -128,7 +107,7 @@


//=========================================================================
// 6. DIRECTORY LOCATION (most people can just ignore this setting)
// 5. DIRECTORY LOCATION (most people can just ignore this setting)
//=========================================================================
// A very few webhosts use /admin as a special URL for you to access a
// control panel or something. Unfortunately this conflicts with the
Expand All @@ -140,7 +119,7 @@


//=========================================================================
// 7. OTHER MISCELLANEOUS SETTINGS (ignore these for new installations)
// 6. OTHER MISCELLANEOUS SETTINGS (ignore these for new installations)
//=========================================================================
//
// These are additional tweaks for which no GUI exists in Moodle yet.
Expand Down Expand Up @@ -471,7 +450,7 @@
// $CFG->svgicons = false;
//
//=========================================================================
// 8. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
// 7. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
//=========================================================================
//
// Force a debugging mode regardless the settings in the site administration
Expand Down Expand Up @@ -512,7 +491,7 @@
// $CFG->showcrondebugging = true;
//
//=========================================================================
// 9. FORCED SETTINGS
// 8. FORCED SETTINGS
//=========================================================================
// It is possible to specify normal admin settings here, the point is that
// they can not be changed through the standard admin settings pages any more.
Expand All @@ -527,12 +506,35 @@
// 'otherplugin' => array('mysetting' => 'myvalue', 'thesetting' => 'thevalue'));
//
//=========================================================================
// 10. PHPUNIT SUPPORT
// 9. PHPUNIT SUPPORT
//=========================================================================
// $CFG->phpunit_prefix = 'phpu_';
// $CFG->phpunit_dataroot = '/home/example/phpu_moodledata';
// $CFG->phpunit_directorypermissions = 02777; // optional
//
//
//=========================================================================
// 10. SECRET PASSWORD SALT
//=========================================================================
// A single site-wide password salt is no longer required *unless* you are
// upgrading an older version of Moodle (prior to 2.5), or if you are using
// a PHP version below 5.3.7. If upgrading, keep any values from your old
// config.php file. If you are using PHP < 5.3.7 set to a long random string
// below:
//
// $CFG->passwordsaltmain = 'a_very_long_random_string_of_characters#@6&*1';
//
// You may also have some alternative salts to allow migration from previously
// used salts.
//
// $CFG->passwordsaltalt1 = '';
// $CFG->passwordsaltalt2 = '';
// $CFG->passwordsaltalt3 = '';
// ....
// $CFG->passwordsaltalt19 = '';
// $CFG->passwordsaltalt20 = '';
//
//
//=========================================================================
// 11. BEHAT SUPPORT
//=========================================================================
Expand Down
6 changes: 5 additions & 1 deletion lib/cronlib.php
Expand Up @@ -216,7 +216,11 @@ function cron_run() {

// note: we can not send emails to suspended accounts
foreach ($newusers as $newuser) {
if (setnew_password_and_mail($newuser)) {
// Use a low cost factor when generating bcrypt hash otherwise
// hashing would be slow when emailing lots of users. Hashes
// will be automatically updated to a higher cost factor the first
// time the user logs in.
if (setnew_password_and_mail($newuser, true)) {
unset_user_preference('create_password', $newuser);
set_user_preference('auth_forcepasswordchange', 1, $newuser);
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/db/install.xml
Expand Up @@ -753,7 +753,7 @@
<FIELD NAME="suspended" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="suspended flag prevents users to log in"/>
<FIELD NAME="mnethostid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="username" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="password" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="password" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="idnumber" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="firstname" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="lastname" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
Expand Down
12 changes: 12 additions & 0 deletions lib/db/upgrade.php
Expand Up @@ -1564,6 +1564,18 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2012120300.07);
}

if ($oldversion < 2013020900.00) {

// Changing precision of field password on table user to (255).
$table = new xmldb_table('user');
$field = new xmldb_field('password', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, 'username');

// Launch change of precision for field password.
$dbman->change_field_precision($table, $field);

// Main savepoint reached.
upgrade_main_savepoint(true, 2013020900.00);
}

return true;
}
5 changes: 4 additions & 1 deletion lib/installlib.php
Expand Up @@ -233,7 +233,10 @@ function install_generate_configphp($database, $cfg) {
}
$configphp .= '$CFG->directorypermissions = ' . $chmod . ';' . PHP_EOL . PHP_EOL;

$configphp .= '$CFG->passwordsaltmain = '.var_export(complex_random_string(), true) . ';' . PHP_EOL . PHP_EOL;
// A site-wide salt is only needed if bcrypt is not properly supported by the current version of PHP.
if (password_compat_not_supported()) {
$configphp .= '$CFG->passwordsaltmain = '.var_export(complex_random_string(), true) . ';' . PHP_EOL . PHP_EOL;
}

$configphp .= 'require_once(dirname(__FILE__) . \'/lib/setup.php\');' . PHP_EOL . PHP_EOL;
$configphp .= '// There is no php closing tag in this file,' . PHP_EOL;
Expand Down

0 comments on commit ec2d8ce

Please sign in to comment.