Switch passwords and secret answers to scrypt

This commit is contained in:
Travis Burtrum 2016-04-28 20:58:40 -04:00
parent c00297da84
commit fb9cf72df9
9 changed files with 319 additions and 126 deletions

View File

@ -258,131 +258,33 @@ function Login2()
$user_settings = $smcFunc['db_fetch_assoc']($request);
$smcFunc['db_free_result']($request);
// Load scrypt stuff.
require_once($sourcedir . '/scrypt.php');
// Figure out the password using SMF's encryption - if what they typed is right.
if (isset($_POST['hash_passwrd']) && strlen($_POST['hash_passwrd']) == 40)
{
// Needs upgrading?
if (strlen($user_settings['passwd']) != 40)
{
$sha_passwd = $_POST['hash_passwrd'];
// default SMF checked to see here if $user_settings['passwd'] was sha1 and disabled javascript hashing if not, like so:
/*
$context['login_errors'] = array($txt['login_hash_error']);
$context['disable_login_hashing'] = true;
unset($user_settings);
return;
}
// Challenge passed.
elseif ($_POST['hash_passwrd'] == sha1($user_settings['passwd'] . $sc))
$sha_passwd = $user_settings['passwd'];
else
{
// Don't allow this!
validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']);
$_SESSION['failed_login'] = @$_SESSION['failed_login'] + 1;
if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold'])
redirectexit('action=reminder');
else
{
log_error($txt['incorrect_password'] . ' - <span class="remove">' . $user_settings['member_name'] . '</span>', 'user');
$context['disable_login_hashing'] = true;
$context['login_errors'] = array($txt['incorrect_password']);
unset($user_settings);
return;
}
}
*/
// I'm leaving this off though, I converted whole db to scrypt, so we always want javascript to hash!
}
else
$sha_passwd = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd']));
// Bad password! Thought you could fool the database?!
if ($user_settings['passwd'] != $sha_passwd)
if (!Password::check($sha_passwd, $user_settings['passwd']))
{
// Let's be cautious, no hacking please. thanx.
validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']);
// Maybe we were too hasty... let's try some other authentication methods.
$other_passwords = array();
// None of the below cases will be used most of the time (because the salt is normally set.)
if ($user_settings['password_salt'] == '')
{
// YaBB SE, Discus, MD5 (used a lot), SHA-1 (used some), SMF 1.0.x, IkonBoard, and none at all.
$other_passwords[] = crypt($_POST['passwrd'], substr($_POST['passwrd'], 0, 2));
$other_passwords[] = crypt($_POST['passwrd'], substr($user_settings['passwd'], 0, 2));
$other_passwords[] = md5($_POST['passwrd']);
$other_passwords[] = sha1($_POST['passwrd']);
$other_passwords[] = md5_hmac($_POST['passwrd'], strtolower($user_settings['member_name']));
$other_passwords[] = md5($_POST['passwrd'] . strtolower($user_settings['member_name']));
$other_passwords[] = md5(md5($_POST['passwrd']));
$other_passwords[] = $_POST['passwrd'];
// This one is a strange one... MyPHP, crypt() on the MD5 hash.
$other_passwords[] = crypt(md5($_POST['passwrd']), md5($_POST['passwrd']));
// Snitz style - SHA-256. Technically, this is a downgrade, but most PHP configurations don't support sha256 anyway.
if (strlen($user_settings['passwd']) == 64 && function_exists('mhash') && defined('MHASH_SHA256'))
$other_passwords[] = bin2hex(mhash(MHASH_SHA256, $_POST['passwrd']));
// phpBB3 users new hashing. We now support it as well ;).
$other_passwords[] = phpBB3_password_check($_POST['passwrd'], $user_settings['passwd']);
// APBoard 2 Login Method.
$other_passwords[] = md5(crypt($_POST['passwrd'], 'CRYPT_MD5'));
}
// The hash should be 40 if it's SHA-1, so we're safe with more here too.
elseif (strlen($user_settings['passwd']) == 32)
{
// vBulletin 3 style hashing? Let's welcome them with open arms \o/.
$other_passwords[] = md5(md5($_POST['passwrd']) . $user_settings['password_salt']);
// Hmm.. p'raps it's Invision 2 style?
$other_passwords[] = md5(md5($user_settings['password_salt']) . md5($_POST['passwrd']));
// Some common md5 ones.
$other_passwords[] = md5($user_settings['password_salt'] . $_POST['passwrd']);
$other_passwords[] = md5($_POST['passwrd'] . $user_settings['password_salt']);
}
elseif (strlen($user_settings['passwd']) == 40)
{
// Maybe they are using a hash from before the password fix.
$other_passwords[] = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd']));
// BurningBoard3 style of hashing.
$other_passwords[] = sha1($user_settings['password_salt'] . sha1($user_settings['password_salt'] . sha1($_POST['passwrd'])));
// Perhaps we converted to UTF-8 and have a valid password being hashed differently.
if ($context['character_set'] == 'utf8' && !empty($modSettings['previousCharacterSet']) && $modSettings['previousCharacterSet'] != 'utf8')
{
// Try iconv first, for no particular reason.
if (function_exists('iconv'))
$other_passwords['iconv'] = sha1(strtolower(iconv('UTF-8', $modSettings['previousCharacterSet'], $user_settings['member_name'])) . un_htmlspecialchars(iconv('UTF-8', $modSettings['previousCharacterSet'], $_POST['passwrd'])));
// Say it aint so, iconv failed!
if (empty($other_passwords['iconv']) && function_exists('mb_convert_encoding'))
$other_passwords[] = sha1(strtolower(mb_convert_encoding($user_settings['member_name'], 'UTF-8', $modSettings['previousCharacterSet'])) . un_htmlspecialchars(mb_convert_encoding($_POST['passwrd'], 'UTF-8', $modSettings['previousCharacterSet'])));
}
}
// SMF's sha1 function can give a funny result on Linux (Not our fault!). If we've now got the real one let the old one be valid!
if (strpos(strtolower(PHP_OS), 'win') !== 0)
{
require_once($sourcedir . '/Subs-Compat.php');
$other_passwords[] = sha1_smf(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd']));
}
// Whichever encryption it was using, let's make it use SMF's now ;).
if (in_array($user_settings['passwd'], $other_passwords))
{
$user_settings['passwd'] = $sha_passwd;
$user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4);
// Update the password and set up the hash.
updateMemberData($user_settings['id_member'], array('passwd' => $user_settings['passwd'], 'password_salt' => $user_settings['password_salt'], 'passwd_flood' => ''));
}
// Okay, they for sure didn't enter the password!
else
{
// They've messed up again - keep a count to see if they need a hand.
$_SESSION['failed_login'] = @$_SESSION['failed_login'] + 1;
@ -398,7 +300,6 @@ function Login2()
$context['login_errors'] = array($txt['incorrect_password']);
return;
}
}
}
elseif (!empty($user_settings['passwd_flood']))
{

View File

@ -516,6 +516,10 @@ function loadProfileFields($force_reload = false)
// Set up the new password variable... ready for storage.
$value = sha1(strtolower($cur_profile[\'member_name\']) . un_htmlspecialchars($value));
require_once($sourcedir . \'/scrypt.php\');
$value = Password::hash($value);
return true;
'),
),
@ -618,7 +622,8 @@ function loadProfileFields($force_reload = false)
'value' => '',
'permission' => 'profile_identity',
'input_validate' => create_function('&$value', '
$value = $value != \'\' ? md5($value) : \'\';
require_once($sourcedir . \'/scrypt.php\');
$value = $value != \'\' ? Password::hash(md5($value)) : \'\';
return true;
'),
),
@ -1364,14 +1369,20 @@ function makeCustomFieldChanges($memID, $area, $sanitize = true)
//echo "success!"; exit;
}
if($area != 'register' && ($row['col_name'] == "cust_moparcr") && $value != '' && strlen($value) != 40 && (!isset($user_profile[$memID]['options'][$row['col_name']]) || $user_profile[$memID]['options'][$row['col_name']] != $value)){
if($area != 'register' && ($row['col_name'] == "cust_moparcr") && $value != '' && strlen($value) < 31 && (!isset($user_profile[$memID]['options'][$row['col_name']]) || $user_profile[$memID]['options'][$row['col_name']] != $value)){
//print_r($user_info);echo("--------------------------------------------------------------\n");print_r($user_profile);exit;
//die(strlen($value)."bob");
if(strlen($value) > 30)
die("<html>Error: Maximum length for MoparCraft server password is 30 characters.</html>");
$value = sha1(strtolower($user_profile[$memID]['member_name']) . htmlspecialchars_decode($value));
if($user_info['id'] == $memID && $value == $user_info['passwd'])
global $sourcedir;
require_once($sourcedir . '/scrypt.php');
if($user_info['id'] == $memID && Password::check($value, $user_info['passwd']))
die("<html>Error: You can't set your MoparCraft server password to be the same as your forum password, if you want to use your forum password, leave this blank.</html>");
$value = Password::hash($value);
}
//xxx end
}
@ -1862,10 +1873,17 @@ function authentication($memID, $saving = false)
// Go then.
$passwd = sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd1']));
require_once($sourcedir . '/scrypt.php');
$passwd = Password::hash($passwd);
// Do the important bits.
updateMemberData($memID, array('openid_uri' => '', 'passwd' => $passwd));
if ($context['user']['is_owner'])
setLoginCookie(60 * $modSettings['cookieTime'], $memID, sha1(sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd2'])) . $cur_profile['password_salt']));
if ($context['user']['is_owner']) {
global $user_settings;
$user_settings['passwd'] = $passwd;
setLoginCookie(60 * $modSettings['cookieTime'], $memID, sha1($passwd . $cur_profile['password_salt']));
}
redirectexit('action=profile;u=' . $memID);
}
@ -3086,7 +3104,9 @@ function profileReloadUser()
if (isset($_POST['passwrd2']) && $_POST['passwrd2'] != '')
{
require_once($sourcedir . '/Subs-Auth.php');
setLoginCookie(60 * $modSettings['cookieTime'], $context['id_member'], sha1(sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd2'])) . $cur_profile['password_salt']));
global $user_settings;
setLoginCookie(60 * $modSettings['cookieTime'], $context['id_member'], sha1($user_settings['passwd'] . $cur_profile['password_salt']));
}
loadUserSettings();

View File

@ -510,7 +510,8 @@ function ModifyProfile($post_errors = array())
$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($cur_profile['member_name'], $_POST['oldpasswrd'], false)), true);
// Bad password!!!
if (!$good_password && $user_info['passwd'] != sha1(strtolower($cur_profile['member_name']) . $_POST['oldpasswrd']))
require_once($sourcedir . '/scrypt.php');
if (!$good_password && !Password::check(sha1(strtolower($cur_profile['member_name']) . $_POST['oldpasswrd']), $user_info['passwd']))
$post_errors[] = 'bad_password';
// Warn other elements not to jump the gun and do custom changes!

View File

@ -299,8 +299,11 @@ function Register2($verifiedOpenID = false)
'hide_email', 'show_online',
);
if (isset($_POST['secret_answer']) && $_POST['secret_answer'] != '')
if (isset($_POST['secret_answer']) && $_POST['secret_answer'] != '') {
$_POST['secret_answer'] = md5($_POST['secret_answer']);
require_once($sourcedir . '/scrypt.php');
$_POST['secret_answer'] = Password::hash($_POST['secret_answer']);
}
// Needed for isReservedName() and registerMember().
require_once($sourcedir . '/Subs-Members.php');
@ -509,6 +512,8 @@ function Register2($verifiedOpenID = false)
if($value == $regOptions['password'])
die("<html>Error: You can't set your MoparCraft server password to be the same as your forum password, if you want to use your forum password, leave this blank.</html>");
$value = sha1(strtolower($regOptions['username']) . htmlspecialchars_decode($value));
require_once($sourcedir . '/scrypt.php');
$value = Password::hash($value);
$_POST['customfield'][$row['col_name']] = $value;
}
// xxx end if we are editing our minecraft name, make sure there are no duplicates
@ -594,6 +599,7 @@ function Register2($verifiedOpenID = false)
{
call_integration_hook('integrate_activate', array($row['member_name']));
// todo: set scrypt here, but we don't use this reg method so whatever
setLoginCookie(60 * $modSettings['cookieTime'], $memberID, sha1(sha1(strtolower($regOptions['username']) . $regOptions['password']) . $regOptions['register_vars']['password_salt']));
redirectexit('action=login2;sa=check;member=' . $memberID, $context['server']['needs_login_fix']);
@ -647,8 +653,10 @@ function Activate()
$row = $smcFunc['db_fetch_assoc']($request);
$smcFunc['db_free_result']($request);
require_once($sourcedir . '/scrypt.php');
// Change their email address? (they probably tried a fake one first :P.)
if (isset($_POST['new_email'], $_REQUEST['passwd']) && sha1(strtolower($row['member_name']) . $_REQUEST['passwd']) == $row['passwd'] && ($row['is_activated'] == 0 || $row['is_activated'] == 2))
if (isset($_POST['new_email'], $_REQUEST['passwd']) && Password::check(sha1(strtolower($row['member_name']) . $_REQUEST['passwd']), $row['passwd']) && ($row['is_activated'] == 0 || $row['is_activated'] == 2))
{
if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == 3)
fatal_lang_error('no_access', false);
@ -915,8 +923,8 @@ function RegisterCheckUsername()
$context['checked_username'] = $smcFunc['htmltrim']($smcFunc['substr']($context['checked_username'], 0, 25));
//xxx only allow these characters
if(!RegisterUserIsAllowed($context['checked_username'], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ /-:.%0123456789_"))
$context['valid_username'] = false;
//if(!RegisterUserIsAllowed($context['checked_username'], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ /-:.%0123456789_"))
// $context['valid_username'] = false;
// Only these characters are permitted.
if (preg_match('~[<>&"\'=\\\]~', preg_replace('~&#(?:\\d{1,7}|x[0-9a-fA-F]{1,6});~', '', $context['checked_username'])) != 0 || $context['checked_username'] == '_' || $context['checked_username'] == '|' || strpos($context['checked_username'], '[code') !== false || strpos($context['checked_username'], '[/code') !== false)

View File

@ -260,7 +260,8 @@ function setPassword2()
validatePasswordFlood($_POST['u'], $flood_value, true);
// User validated. Update the database!
updateMemberData($_POST['u'], array('validation_code' => '', 'passwd' => sha1(strtolower($username) . $_POST['passwrd1'])));
require_once($sourcedir . '/scrypt.php');
updateMemberData($_POST['u'], array('validation_code' => '', 'passwd' => Password::hash(sha1(strtolower($username) . $_POST['passwrd1']))));
call_integration_hook('integrate_reset_pass', array($username, $username, $_POST['passwrd1']));
@ -348,7 +349,8 @@ function SecretAnswer2()
$smcFunc['db_free_result']($request);
// Check if the secret answer is correct.
if ($row['secret_question'] == '' || $row['secret_answer'] == '' || md5($_POST['secret_answer']) != $row['secret_answer'])
require_once($sourcedir . '/scrypt.php');
if ($row['secret_question'] == '' || $row['secret_answer'] == '' || !Password::check(md5($_POST['secret_answer']), $row['secret_answer']))
{
log_error(sprintf($txt['reminder_error'], $row['member_name']), 'user');
fatal_lang_error('incorrect_answer', false);
@ -379,7 +381,8 @@ function SecretAnswer2()
fatal_lang_error('profile_error_password_' . $passwordError, false);
// Alright, so long as 'yer sure.
updateMemberData($row['id_member'], array('passwd' => sha1(strtolower($row['member_name']) . $_POST['passwrd1'])));
require_once($sourcedir . '/scrypt.php');
updateMemberData($row['id_member'], array('passwd' => Password::hash(sha1(strtolower($row['member_name']) . $_POST['passwrd1']))));
call_integration_hook('integrate_reset_pass', array($row['member_name'], $row['member_name'], $_POST['passwrd1']));

View File

@ -582,6 +582,9 @@ function resetPassword($memID, $username = null)
$newPassword = substr(preg_replace('/\W/', '', md5(mt_rand())), 0, 10);
$newPassword_sha1 = sha1(strtolower($user) . $newPassword);
require_once($sourcedir . '/scrypt.php');
$newPassword_sha1 = Password::hash($newPassword_sha1);
// Do some checks on the username if needed.
if ($username !== null)
{

View File

@ -676,6 +676,9 @@ function registerMember(&$regOptions, $return_errors = false)
'openid_uri' => (!empty($regOptions['openid']) ? $regOptions['openid'] : ''),
);
require_once($sourcedir . '/scrypt.php');
$regOptions['register_vars']['passwd'] = Password::hash($regOptions['register_vars']['passwd']);
// Setup the activation status on this new account so it is correct - firstly is it an under age account?
if ($regOptions['require'] == 'coppa')
{

253
Sources/scrypt.php Normal file
View File

@ -0,0 +1,253 @@
<?php
/**
* This file contains an example helper classes for the php-scrypt extension.
*
* As with all cryptographic code; it is recommended that you use a tried and
* tested library which uses this library; rather than rolling your own.
*
* PHP version 5
*
* @category Security
* @package Scrypt
* @author Dominic Black <thephenix@gmail.com>
* @license http://www.opensource.org/licenses/BSD-2-Clause BSD 2-Clause License
* @link http://github.com/DomBlack/php-scrypt
*/
/**
* This class abstracts away from scrypt module, allowing for easy use.
*
* You can create a new hash for a password by calling Password::hash($password)
*
* You can check a password by calling Password::check($password, $hash)
*
* @category Security
* @package Scrypt
* @author Dominic Black <thephenix@gmail.com>
* @license http://www.opensource.org/licenses/BSD-2-Clause BSD 2-Clause License
* @link http://github.com/DomBlack/php-scrypt
*/
abstract class Password
{
/**
*
* @var int The key length
*/
private static $_keyLength = 32;
/**
* Get the byte-length of the given string
*
* @param string $str Input string
*
* @return int
*/
protected static function strlen( $str ) {
static $isShadowed = null;
if ($isShadowed === null) {
$isShadowed = extension_loaded('mbstring') &&
ini_get('mbstring.func_overload') & 2;
}
if ($isShadowed) {
return mb_strlen($str, '8bit');
} else {
return strlen($str);
}
}
/**
* Generates a random salt
*
* @param int $length The length of the salt
*
* @return string The salt
*/
public static function generateSalt($length = 8)
{
$buffer = '';
$buffer_valid = false;
if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
$buffer = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
if ($buffer) {
$buffer_valid = true;
}
}
if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
$cryptoStrong = false;
$buffer = openssl_random_pseudo_bytes($length, $cryptoStrong);
if ($buffer && $cryptoStrong) {
$buffer_valid = true;
}
}
if (!$buffer_valid && is_readable('/dev/urandom')) {
$f = fopen('/dev/urandom', 'r');
$read = static::strlen($buffer);
while ($read < $length) {
$buffer .= fread($f, $length - $read);
$read = static::strlen($buffer);
}
fclose($f);
if ($read >= $length) {
$buffer_valid = true;
}
}
if (!$buffer_valid || static::strlen($buffer) < $length) {
$bl = static::strlen($buffer);
for ($i = 0; $i < $length; $i++) {
if ($i < $bl) {
$buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
} else {
$buffer .= chr(mt_rand(0, 255));
}
}
}
$salt = str_replace(array('+', '$'), array('.', ''), base64_encode($buffer));
return $salt;
}
/**
* Create a password hash
*
* @param string $password The clear text password
* @param string $salt The salt to use, or null to generate a random one
* @param int $N The CPU difficultly (must be a power of 2, > 1)
* @param int $r The memory difficultly
* @param int $p The parallel difficultly
*
* @return string The hashed password
*/
public static function hash($password, $salt = false, $N = 32768, $r = 8, $p = 1)
{
if ($N == 0 || ($N & ($N - 1)) != 0) {
throw new \InvalidArgumentException("N must be > 0 and a power of 2");
}
if ($N > PHP_INT_MAX / 128 / $r) {
throw new \InvalidArgumentException("Parameter N is too large");
}
if ($r > PHP_INT_MAX / 128 / $p) {
throw new \InvalidArgumentException("Parameter r is too large");
}
if ($salt === false) {
$salt = self::generateSalt();
} else {
// Remove dollar signs from the salt, as we use that as a separator.
$salt = str_replace(array('+', '$'), array('.', ''), base64_encode($salt));
}
$hash = scrypt($password, $salt, $N, $r, $p, self::$_keyLength);
return $N . '$' . $r . '$' . $p . '$' . $salt . '$' . $hash;
}
/**
* Calculates a hash from a clear text password using the info from a previous hash
*
* DO NOT USE except for legacy applications, use check() instead, because this is
* vulnerable to timing attacks if you use == to compare strings
*
* @param string $password The clear text password
* @param string $hash The hashed password
*
* @return boolean If the clear text matches
*/
public static function calculateHash($password, $hash)
{
// Is there actually a hash?
if (!$hash) {
return false;
}
list ($N, $r, $p, $salt, $hash) = explode('$', $hash);
// No empty fields?
if (empty($N) or empty($r) or empty($p) or empty($salt) or empty($hash)) {
return false;
}
// Are numeric values numeric?
if (!is_numeric($N) or !is_numeric($r) or !is_numeric($p)) {
return false;
}
$calculated = scrypt($password, $salt, $N, $r, $p, self::$_keyLength);
return $N . '$' . $r . '$' . $p . '$' . $salt . '$' . $calculated;
}
/**
* Check a clear text password against a hash
*
* @param string $password The clear text password
* @param string $hash The hashed password
*
* @return boolean If the clear text matches
*/
public static function check($password, $hash)
{
// Is there actually a hash?
if (!$hash) {
return false;
}
list ($N, $r, $p, $salt, $hash) = explode('$', $hash);
// No empty fields?
if (empty($N) or empty($r) or empty($p) or empty($salt) or empty($hash)) {
return false;
}
// Are numeric values numeric?
if (!is_numeric($N) or !is_numeric($r) or !is_numeric($p)) {
return false;
}
$calculated = scrypt($password, $salt, $N, $r, $p, self::$_keyLength);
// Use compareStrings to avoid timeing attacks
return self::compareStrings($hash, $calculated);
}
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*
* Compare two strings to avoid timing attacks
*
* C function memcmp() internally used by PHP, exits as soon as a difference
* is found in the two buffers. That makes possible of leaking
* timing information useful to an attacker attempting to iteratively guess
* the unknown string (e.g. password).
*
* @param string $expected
* @param string $actual
*
* @return boolean If the two strings match.
*/
public static function compareStrings($expected, $actual)
{
$expected = (string) $expected;
$actual = (string) $actual;
$lenExpected = static::strlen($expected);
$lenActual = static::strlen($actual);
$len = min($lenExpected, $lenActual);
$result = 0;
for ($i = 0; $i < $len; $i ++) {
$result |= ord($expected[$i]) ^ ord($actual[$i]);
}
$result |= $lenExpected ^ $lenActual;
return ($result === 0);
}
}

View File

@ -634,7 +634,7 @@ function hashLoginPassword(doForm, cur_session_id)
if (!('opera' in window))
doForm.passwrd.autocomplete = 'off';
doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id);
doForm.hash_passwrd.value = hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit());
// It looks nicer to fill it with asterisks, but Firefox will try to save that.
if (is_ff != -1)
@ -652,7 +652,8 @@ function hashAdminPassword(doForm, username, cur_session_id)
if (typeof(hex_sha1) == 'undefined')
return;
doForm.admin_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.admin_pass.value.php_to8bit()) + cur_session_id);
doForm.admin_hash_pass.value = hex_sha1(username.php_to8bit().php_strtolower() + doForm.admin_pass.value.php_to8bit());
doForm.admin_pass.value = doForm.admin_pass.value.replace(/./g, '*');
}