XMB Forum Software

[1.9.12] More Secure Passwords (SHA-256)

bfgadmin - 12-5-2020 at 05:38 AM

Still have a few minor issues to work out, including the initial login by an existing MD5 user. Will post the update for this shortly.

This mod is tested & working on XMB 1.9.11 and the new 1.9.12. It replaces the traditional MD5 password hash with the more secure SHA-256 algorithm. It also requires existing users to change (update) their password, at which point it will be stored as a salted SHA-256 hash over the less secure MD5.

There are several steps involved in this hack, and the very first is taking a full backup. File system, Database, Templates and anything else related to your forum.

After your full backup is complete (don't skip this step!), you will need to sign into an account with Super Admin access and select the "Insert Raw SQL" option. Execute the following query:

Code:
ALTER TABLE $table_members MODIFY password varchar(64) NOT NULL


This will enlarge the password column size in order to accommodate the 256 bit hashes.

Now it is time to dive into some code. We'll need to create a file called "updatepw.php" at some point. Might as well do it now. Create updatepw.php in the same directory as header.php, index.php, etc. Here are the contents of updatepw.php:

Code:
<?php define('X_SCRIPT', 'updatepw.php'); require 'header.php'; if (!X_MEMBER) { header('HTTP/1.0 403 Forbidden'); loadtemplates('misc_feature_notavailable'); eval('$css = "'.template('css').'";'); nav('Update Password'); eval('$header = "'.template('header').'";'); eval('$featureoff = "'.template('misc_feature_notavailable').'";'); end_time(); eval('$footer = "'.template('footer').'";'); echo $header; echo "<style>".$css."</style>"; echo $featureoff, $footer; exit(); } if (X_MEMBER) { $query = $db->query("SELECT COUNT(*) FROM ".X_PREFIX."members WHERE username='$xmbuser' AND LENGTH(password) > '33'"); $result = $db->result($query, 0); $db->free_result($query); if($result > 0) { header('HTTP/1.0 403 Forbidden'); loadtemplates('misc_feature_notavailable'); //eval('$css = "'.template('css').'";'); nav('Update Password'); eval('$header = "'.template('header').'";'); eval('$featureoff = "'.template('misc_feature_notavailable').'";'); end_time(); eval('$footer = "'.template('footer').'";'); echo $header; echo "<style>".$css."</style>"; echo $featureoff, $footer; exit(); } } nav('One-Time Password Update'); eval('$header = "'.template('header').'";'); eval('$css = "'.template('css').'";'); end_time(); eval('$footer = "'.template('footer').'";'); echo $header; echo "<style>".$css."</style>"; if(isset($_POST['updatepw']) && isset($_POST['updatepwcf'])) { //Form submitted if($_POST['updatepw'] != $_POST['updatepwcf']) { echo "<center><font class='mediumtxt'><b>The passwords you typed do not match!</b></font></center><br>"; echo "<center><form method='post' action='updatepw.php?runonce=true'>"; echo "<font class='smalltxt'>Enter new password: <input type='password' name='updatepw'> Enter again to confirm: <input type='password' name='updatepwcf'></font>"; echo " <input type='submit' name='updatebtn' value='Update'></form></center>"; }elseif(strlen($_POST['updatepw']) < 7 || strlen($_POST['updatepwcf']) < 7){ echo "<center><font class='mediumtxt'><b>The password you selected is too short! Longer passwords are more secure. Enter at least 7 characters.</b></font></center><br>"; echo "<center><form method='post' action='updatepw.php?runonce=true'>"; echo "<font class='smalltxt'>Enter new password: <input type='password' name='updatepw'> Enter again to confirm: <input type='password' name='updatepwcf'></font>"; echo " <input type='submit' name='updatebtn' value='Update'></form></center>"; }else{ $betterpassword = hash("sha256", $_POST['updatepw'] . $self['regdate'] . $self['regip']); $db->query("UPDATE ".X_PREFIX."members SET password='$betterpassword' WHERE username='$xmbuser' LIMIT 1"); echo "<center><font class='mediumtxt'><b>Your password has been updated! Please <a href='index.php'>return to the forum index</a> and login using your new password.</b></font></center><br>"; } }else{ //Collect new PW combo from form echo "<center><form method='post' action='updatepw.php?runonce=true'>"; echo "<font class='smalltxt'>Enter new password: <input type='password' name='updatepw'> Enter again to confirm: <input type='password' name='updatepwcf'></font>"; echo " <input type='submit' name='updatebtn' value='Update'></form></center>"; } echo $footer; ?>


(continued below)

bfgadmin - 12-5-2020 at 05:49 AM

Our next step is update header.php.

Please open header.php:

Find
Code:
// check for new u2u's


Add above

Code:
//Determine whether client's password is hashed using MD5 or SHA256 if(X_MEMBER && !isset($_GET['runonce'])) { $query = $db->query("SELECT COUNT(*) FROM ".X_PREFIX."members WHERE username='$xmbuser' AND LENGTH(password) < '33'"); $result = $db->result($query, 0); $db->free_result($query); if($result > 0) { eval('$header = "'.template('header').'";'); eval('$css = "'.template('css').'";'); eval('$footer = "'.template('footer').'";'); echo "<style>".$css."</style>"; echo $header; echo "<center><b>Your password has expired!</b> You must <a href='updatepw.php?runonce=true'><b>Click here</b></a> to update your password before continuing.</center>"; echo $footer; exit; } }


Next, open cp.php:

Find
Code:
$newpw = md5($_POST['pw'.$mem['uid']]);


Replace with
Code:
$salt = $mem['regdate'] . $mem['regip']; $newpw = hash("sha256", $_POST['pw'.$mem['uid']] . $salt);


Open editprofile.php

Find
Code:
$newpassword = md5($newpassword);


Replace with
Code:
$salt = $member['regdate'] . $member['regip']; $newpassword = hash("sha256", $newpassword . $salt);


Open member.php

Find
Code:
$self['password'] = md5( $self['password'] );


Replace With
Code:
$salt = $onlinetime . $onlineip; $self['password'] = hash("sha256", $self['password'] . $salt );


(continued below)

bfgadmin - 12-5-2020 at 05:52 AM

Open memcp.php

Find
Code:
if ( $member['password'] != md5($_POST['oldpassword'])) { error($lang['textpwincorrect']); }


Replace With
Code:
$salt = $member['regdate'] . $member['regip']; $pwhash = hash("sha256", $_POST['oldpassword'] . $salt); if ( $member['password'] != $pwhash) { error($lang['textpwincorrect']); }


Find
Code:
$newpassword = md5($_POST['newpassword']);


Replace With
Code:
$newpassword = hash("sha256", $_POST['newpassword'] . $salt);


Open sessions.inc.php (includes/session.inc.php)

Find
Code:
$pinput = md5( $pinput );


Replace With
Code:
$salt = $data->member['regdate'] . $data->member['regip']; //THE SALT VALUE //defeats precomputed hash table lookups and adds to computational expense for attacker $pinput = hash("sha256", $pinput . $salt);

flushedpancake - 4-2-2024 at 05:21 AM

How come you didn't opt for using password_hash() functions built into PHP since 5.5?

This should probably be looked into implementing into a future release though, better security is always good. :D