diff --git a/.gitignore b/.gitignore index 3c86c5f0..15e9bfc9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,6 @@ public/include/config/global.inc.sha.php # IDE Settings /.idea/* -/.buildpath/* -/.project/* -/.settings/* +.buildpath +.project +.settings diff --git a/public/include/classes/base.class.php b/public/include/classes/base.class.php index 85b43d57..d3dd18c1 100644 --- a/public/include/classes/base.class.php +++ b/public/include/classes/base.class.php @@ -31,6 +31,9 @@ class Base { public function setSalt($salt) { $this->salt = $salt; } + public function setSalty($salt) { + $this->salty = $salt; + } public function setSmarty($smarty) { $this->smarty = $smarty; } diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index ba07b950..de2a3266 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -788,6 +788,27 @@ class User extends Base { if ($logout == true) $this->logoutUser($_SERVER['REQUEST_URI']); return false; } + + /** + * Gets the current CSRF token for this user/type setting and time chunk + * @param string User; for hash seed, if username isn't available use IP + * @param string Type of token; for hash seed, should be unique per page/use + * @return string CSRF token + */ + public function getCSRFToken($user, $type) { + $date = date('m/d/y/H/i/s'); + $data = explode('/', $date); + $month = $data[0]; $day = $data[1]; $year = $data[2]; + $hour = $data[3]; $minute = $data[4]; $second = $data[5]; + $seed = $this->salty; + // X second lead time on each minute + if ($minute == 59 && $second > (60-$this->config['csrf']['options']['leadtime'])) { + $minute = 0; + $fhour = ($hour == 23) ? $hour = 0 : $hour+=1; + } + $seed = $seed.$month.$day.$user.$type.$year.$hour.$minute.$seed; + return $this->getHash($seed); + } } // Make our class available automatically @@ -795,6 +816,7 @@ $user = new User(); $user->setDebug($debug); $user->setMysql($mysqli); $user->setSalt(SALT); +$user->setSalty(SALTY); $user->setSmarty($smarty); $user->setConfig($config); $user->setMail($mail); diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 13daf7da..3c6cddae 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -26,6 +26,7 @@ define('DEBUG', 0); // SALT used to hash passwords define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM'); +define('SALTY', 'THISSHOULDALSOBERRAANNDDOOM'); /** * Underlying coin algorithm that you are mining on. Set this to whatever your coin needs: @@ -123,6 +124,28 @@ $config['twofactor']['options']['details'] = true; $config['twofactor']['options']['withdraw'] = true; $config['twofactor']['options']['changepw'] = true; +/** + * CSRF protection config + * + * Explanation: + * To help protect against CSRF, we can generate a hash that changes every minute + * and is unique for each user/IP and page or use, and check against that when + * the form is submitted. + * + * Options: + * enabled = Whether or not we will generate/check for valid CSRF tokens + * leadtime = 1 minute + leadtime seconds for reseeding tokens + * login = Use and check CSRF tokens for the login forms + * + * Default: + * enabled = true + * leadtime = 3 + * login = true + */ +$config['csrf']['enabled'] = true; +$config['csrf']['options']['leadtime'] = 3; +$config['csrf']['forms']['login'] = true; + /** * Lock account after maximum failed logins * diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index fd819f51..b3614a87 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -4,9 +4,8 @@ if (!defined('SECURITY')) die('Hacking attempt'); +// twofactor stuff $cp_editable = $wf_editable = $ea_editable = $wf_sent = $ea_sent = $cp_sent = 0; - -// stupid hack to fix input when an error happened with a valid token $ea_token = (!isset($_POST['ea_token'])) ? '' : $_POST['ea_token']; $cp_token = (!isset($_POST['cp_token'])) ? '' : $_POST['cp_token']; $wf_token = (!isset($_POST['wf_token'])) ? '' : $_POST['wf_token']; @@ -80,7 +79,7 @@ if ($user->isAuthenticated()) { } } } else { - // back to get, was only post to fix for stupid hack + // back to get, was only post to fix for old token $ea_token = (!isset($_GET['ea_token'])) ? '' : $_GET['ea_token']; $cp_token = (!isset($_GET['cp_token'])) ? '' : $_GET['cp_token']; $wf_token = (!isset($_GET['wf_token'])) ? '' : $_GET['wf_token']; @@ -137,7 +136,6 @@ if ($user->isAuthenticated()) { } // 2fa - one last time so we can sync with changes we made during this page if ($user->isAuthenticated() && $config['twofactor']['enabled']) { - // stupid hack part deux // set the token to be the old token so we still have it if it errors out if ($old_token_type == 5) { $ea_token = $old_token; diff --git a/public/include/pages/home.inc.php b/public/include/pages/home.inc.php index ea85e43b..4dffeb8a 100644 --- a/public/include/pages/home.inc.php +++ b/public/include/pages/home.inc.php @@ -22,7 +22,12 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { } else { $debug->append('Using cached page', 3); } - +// csrf token - update if it's enabled +$token = ''; +if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) { + $token = $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login'); +} // Load news entries for Desktop site and unauthenticated users $smarty->assign("CONTENT", "default.tpl"); +$smarty->assign('CTOKEN', $token); ?> diff --git a/public/include/pages/login.inc.php b/public/include/pages/login.inc.php index e7cbaffa..f1758eb4 100644 --- a/public/include/pages/login.inc.php +++ b/public/include/pages/login.inc.php @@ -5,16 +5,37 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserId($_POST['username']))) { $_SESSION['POPUP'][] = array('CONTENT' => 'You are not allowed to login during maintenace.', 'TYPE' => 'info'); -} else if ($user->checkLogin(@$_POST['username'], @$_POST['password']) ) { - empty($_POST['to']) ? $to = $_SERVER['SCRIPT_NAME'] : $to = $_POST['to']; - $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]); - $location = @$_SERVER['HTTPS'] === true ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $to : 'http://' . $_SERVER['SERVER_NAME'] . $port . $to; - if (!headers_sent()) header('Location: ' . $location); - exit(''); +} else if (isset($_POST['username']) && isset($_POST['password'])) { + $nocsrf = 1; + if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) { + if ((isset($_POST['ctoken']) && $_POST['ctoken'] !== $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login')) || (!isset($_POST['ctoken']))) { + // csrf protection is on and this token is invalid, error out -> time expired + $nocsrf = 0; + } + } + if ($nocsrf == 1 || (!$config['csrf']['enabled'] || !$config['csrf']['forms']['login'])) { + $checklogin = $user->checkLogin($_POST['username'], $_POST['password']); + if ($checklogin) { + empty($_POST['to']) ? $to = $_SERVER['SCRIPT_NAME'] : $to = $_POST['to']; + $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]); + $location = @$_SERVER['HTTPS'] === true ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $to : 'http://' . $_SERVER['SERVER_NAME'] . $port . $to; + if (!headers_sent()) header('Location: ' . $location); + exit(''); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: '. $user->getError(), 'TYPE' => 'errormsg'); + } + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Login token expired'. $user->getError(), 'TYPE' => 'errormsg'); + } } else if (@$_POST['username'] && @$_POST['password']) { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: '. $user->getError(), 'TYPE' => 'errormsg'); } - +// csrf token - update if it's enabled +$token = ''; +if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) { + $token = $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login'); +} // Load login template $smarty->assign('CONTENT', 'default.tpl'); +$smarty->assign('CTOKEN', $token); ?> diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 21fbcf2a..f059d477 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -65,6 +65,7 @@ $aGlobal = array( 'reward' => $config['reward'], 'price' => $setting->getValue('price'), 'twofactor' => $config['twofactor'], + 'csrf' => $config['csrf'], 'config' => array( 'disable_navbar' => $setting->getValue('disable_navbar'), 'disable_navbar_api' => $setting->getValue('disable_navbar_api'), diff --git a/public/templates/mpos/login/default.tpl b/public/templates/mpos/login/default.tpl index 031b9ca0..9b8eefec 100644 --- a/public/templates/mpos/login/default.tpl +++ b/public/templates/mpos/login/default.tpl @@ -1,6 +1,7 @@
+ {if $GLOBAL.csrf.enabled && $GLOBAL.csrf.forms.login}{/if}

Login with existing account

diff --git a/public/templates/mpos/login/small.tpl b/public/templates/mpos/login/small.tpl index e02921de..6448f750 100644 --- a/public/templates/mpos/login/small.tpl +++ b/public/templates/mpos/login/small.tpl @@ -2,6 +2,7 @@