mirror of
https://github.com/moparisthebest/moparscape.org-smf
synced 2024-11-21 08:15:09 -05:00
Switch passwords and secret answers to scrypt
This commit is contained in:
parent
c00297da84
commit
fb9cf72df9
@ -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;
|
||||
|
||||
@ -399,7 +301,6 @@ function Login2()
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (!empty($user_settings['passwd_flood']))
|
||||
{
|
||||
// Let's be sure they weren't a little hacker.
|
||||
|
@ -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();
|
||||
|
@ -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!
|
||||
|
@ -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)
|
||||
|
@ -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']));
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
253
Sources/scrypt.php
Normal 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);
|
||||
}
|
||||
}
|
@ -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, '*');
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user