diff --git a/public/include/classes/csrftoken.class.php b/public/include/classes/csrftoken.class.php
index 68f8e427..ca303c7b 100644
--- a/public/include/classes/csrftoken.class.php
+++ b/public/include/classes/csrftoken.class.php
@@ -5,35 +5,69 @@ if (!defined('SECURITY')) die('Hacking attempt');
class CSRFToken Extends Base {
/**
- * Gets a basic CSRF token for this user/type and time chunk
- * @param string user User; for hash seed, if username isn't available use IP
- * @param string type Type of token; for hash seed, should be unique per page/use
- * @param string timing Which date() chars we add to the seed; default month day year hour minute ie same minute only
- * @param string seedExtra Extra information to add to the seed
- * @return string CSRF token
+ * Gets a basic csrf token
+ * @param string $user user or IP/host address
+ * @param string $type page name or other unique per-page identifier
*/
- public function getBasic($user, $type, $timing='mdyHi', $seedExtra='') {
- $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];
- $salt1 = $this->salt; $salt2 = $this->salty; $seed = $salt1;
- $lead = $this->config['csrf']['leadtime'];
- $lead_sec = ($lead <= 11 && $lead >= 0) ? $lead : 3;
- if ($minute == 59 && $second > (60-$lead_sec)) {
- $minute = 0;
- $fhour = ($hour == 23) ? $hour = 0 : $hour+=1;
- }
- $seed.= (strpos($timing, 'm') !== false) ? $month : '';
- $seed.= (strpos($timing, 'd') !== false) ? $day : '';
- $seed.= (strpos($timing, 'y') !== false) ? $year : '';
- $seed.= (strpos($timing, 'H') !== false) ? $hour : '';
- $seed.= (strpos($timing, 'i') !== false) ? $minute : '';
- $seed.= (strpos($timing, 's') !== false) ? $second : '';
- $seed.= ($seedExtra !== '') ? $seedExtra.$salt2 : $salt2;
+ public function getBasic($user, $type) {
+ $date = date('m/d/y/H/i');
+ $d = explode('/', $date);
+ $seed = $this->buildSeed($user.$type, $d[0], $d[1], $d[2], $d[3], $d[4]);
return $this->getHash($seed);
}
+ /**
+ * Returns +1 min and +1 hour rollovers hashes
+ * @param string $user user or IP/host address
+ * @param string $type page name or other unique per-page identifier
+ * @return array 1min and 1hour hashes
+ */
+ public function checkAdditional($user, $type) {
+ $date = date('m/d/y/H/i');
+ $d = explode('/', $date);
+ // minute may have rolled over
+ $seed1 = $this->buildSeed($user.$type, $d[0], $d[1], $d[2], $d[3], ($d[4]-1));
+ // hour may have rolled over
+ $seed2 = $this->buildSeed($user.$type, $d[0], $d[1], $d[2], ($d[3]-1), 59);
+ return array($this->getHash($seed1), $this->getHash($seed2));
+ }
+
+ /**
+ * Builds a seed with the given data
+ * @param string $data
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ * @param int $hour
+ * @param int $minute
+ * @return string seed
+ */
+ private function buildSeed($data, $year, $month, $day, $hour, $minute) {
+ return $this->salty.$year.$month.$day.$data.$hour.$minute.$this->salt;
+ }
+
+ /**
+ * Checks if the token is correct as is, if not checks for rollovers with checkAdditional()
+ * @param string $user user or IP/host address
+ * @param string $type page name or other unique per-page identifier
+ * @param string $token token to check against
+ * @return boolean
+ */
+ public function checkBasic($user, $type, $token) {
+ if (empty($token)) return false;
+ $token_now = $this->getBasic($user, $type);
+ if ($token_now !== $token) {
+ $tokens_check = $this->checkAdditional($user, $type);
+ $match = 0;
+ foreach ($tokens_check as $checkit) {
+ if ($checkit == $token) $match = 1;
+ }
+ return ($match) ? true : false;
+ } else {
+ return true;
+ }
+ }
+
/**
* Convenience method to get a token expired message with a token type, and ? image with description
* @param string $tokentype if you want a specific tokentype, set it here
diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php
index f279963b..8742bcb3 100644
--- a/public/include/config/global.inc.dist.php
+++ b/public/include/config/global.inc.dist.php
@@ -135,17 +135,13 @@ $config['twofactor']['options']['changepw'] = true;
*
* Options:
* enabled = Whether or not we will generate/check for valid CSRF tokens
- * leadtime = Length of time in seconds to give as leeway between minute switches
- * * Don't change this unless you know why you're changing it
* disabled_forms = Which forms you want to disable csrf protection on, if enabled
* * Valid options : login, contact, accountedit, workers, notifications, invite, register, passreset, unlockaccount
* Default:
* enabled = true
- * leadtime = 3
* disabled_forms = array();
*/
$config['csrf']['enabled'] = true;
-$config['csrf']['leadtime'] = 3;
$config['csrf']['disabled_forms'] = array();
/**
diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php
index 5fbc8d0a..133687e6 100644
--- a/public/include/pages/account/edit.inc.php
+++ b/public/include/pages/account/edit.inc.php
@@ -16,7 +16,7 @@ $updating = (@$_POST['do']) ? 1 : 0;
// csrf stuff
$csrfenabled = ($config['csrf']['enabled'] && !in_array('accountedit', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'editaccount') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'editaccount', @$_POST['ctoken'])) ? 1 : 0;
}
if ($user->isAuthenticated()) {
diff --git a/public/include/pages/account/invitations.inc.php b/public/include/pages/account/invitations.inc.php
index e3328f01..10a85149 100644
--- a/public/include/pages/account/invitations.inc.php
+++ b/public/include/pages/account/invitations.inc.php
@@ -8,7 +8,7 @@ if ($user->isAuthenticated()) {
// csrf stuff
$csrfenabled = ($config['csrf']['enabled'] && !in_array('invitations', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'invitations') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'invitations', @$_POST['ctoken'])) ? 1 : 0;
}
if ($invitation->getCountInvitations($_SESSION['USERDATA']['id']) >= $config['accounts']['invitations']['count']) {
$_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the allowed invitations of ' . $config['accounts']['invitations']['count'], 'TYPE' => 'errormsg');
diff --git a/public/include/pages/account/notifications.inc.php b/public/include/pages/account/notifications.inc.php
index ff2f7c28..8de15337 100644
--- a/public/include/pages/account/notifications.inc.php
+++ b/public/include/pages/account/notifications.inc.php
@@ -10,7 +10,7 @@ if ($user->isAuthenticated()) {
// csrf stuff
$csrfenabled = ($config['csrf']['enabled'] && !in_array('notifications', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'editnotifs') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'editnotifs', @$_POST['ctoken'])) ? 1 : 0;
}
if (@$_REQUEST['do'] == 'save') {
diff --git a/public/include/pages/account/unlock.inc.php b/public/include/pages/account/unlock.inc.php
index 3fb4667e..9ab0551f 100644
--- a/public/include/pages/account/unlock.inc.php
+++ b/public/include/pages/account/unlock.inc.php
@@ -6,7 +6,7 @@ if (!defined('SECURITY')) die('Hacking attempt');
// csrf stuff
$csrfenabled = ($config['csrf']['enabled'] && !in_array('unlockaccount', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'unlockaccount') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'unlockaccount', @$_POST['ctoken'])) ? 1 : 0;
}
// Confirm an account by token
diff --git a/public/include/pages/account/workers.inc.php b/public/include/pages/account/workers.inc.php
index 42e03b3e..89dddf9f 100644
--- a/public/include/pages/account/workers.inc.php
+++ b/public/include/pages/account/workers.inc.php
@@ -6,7 +6,7 @@ if ($user->isAuthenticated()) {
// csrf stuff
$csrfenabled = ($config['csrf']['enabled'] && !in_array('workers', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'workers') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'workers', @$_POST['ctoken'])) ? 1 : 0;
}
switch (@$_REQUEST['do']) {
diff --git a/public/include/pages/contactform/contactform.inc.php b/public/include/pages/contactform/contactform.inc.php
index 3c85df09..d734874e 100644
--- a/public/include/pages/contactform/contactform.inc.php
+++ b/public/include/pages/contactform/contactform.inc.php
@@ -17,7 +17,7 @@ if ($setting->getValue('recaptcha_enabled')) {
// csrf if enabled
$csrfenabled = ($config['csrf']['enabled'] && !in_array('contact', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'contact') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'contact', @$_POST['ctoken'])) ? 1 : 0;
}
if ($setting->getValue('disable_contactform')) {
diff --git a/public/include/pages/login.inc.php b/public/include/pages/login.inc.php
index 2485a84c..30f3b832 100644
--- a/public/include/pages/login.inc.php
+++ b/public/include/pages/login.inc.php
@@ -6,7 +6,7 @@ if (!defined('SECURITY')) die('Hacking attempt');
// csrf if enabled
$csrfenabled = ($config['csrf']['enabled'] && !in_array('login', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'login') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'login', @$_POST['ctoken'])) ? 1 : 0;
}
// ReCaptcha handling if enabled
@@ -21,7 +21,6 @@ if ($setting->getValue('recaptcha_enabled') && $setting->getValue('recaptcha_ena
( (isset($_POST["recaptcha_response_field"])) ? $_POST["recaptcha_response_field"] : null )
);
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key'), $rsp->error, true));
- if (!$rsp->is_valid) $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again.', 'TYPE' => 'errormsg');
} else {
$smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key'), null, true));
}
@@ -30,36 +29,23 @@ if ($setting->getValue('recaptcha_enabled') && $setting->getValue('recaptcha_ena
if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserIdByEmail($_POST['username']))) {
$_SESSION['POPUP'][] = array('CONTENT' => 'You are not allowed to login during maintenace.', 'TYPE' => 'info');
} else if (!empty($_POST['username']) && !empty($_POST['password'])) {
- $nocsrf = 1;
- $recaptchavalid = 0;
- if ($setting->getValue('recaptcha_enabled') && $setting->getValue('recaptcha_enabled_logins') && $rsp->is_valid) {
- if ($rsp->is_valid) {
- // recaptcha is enabled and valid
- $recaptchavalid = 1;
- } else {
- // error out, invalid captcha
- $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: The captcha you entered was incorrect', 'TYPE' => 'errormsg');
- }
- }
- if ($config['csrf']['enabled'] && !in_array('login', $config['csrf']['disabled_forms'])) {
- if ((isset($_POST['ctoken']) && $_POST['ctoken'] !== $csrftoken->getBasic($user->getCurrentIP(), 'login')) || (!isset($_POST['ctoken']))) {
- // csrf protection is on and this token is invalid, error out -> time expired
- $nocsrf = 0;
- }
- }
// Check if recaptcha is enabled, process form data if valid
- if (($setting->getValue('recaptcha_enabled') != 1 || $setting->getValue('recaptcha_enabled_logins') != 1 || $rsp->is_valid) && ($nocsrf == 1 || (!$config['csrf']['enabled'] || in_array('login', $config['csrf']['disabled_forms'])))) {
- 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('');
+ if (!$setting->getValue('recaptcha_enabled') || !$setting->getValue('recaptcha_enabled_logins') || ($setting->getValue('recaptcha_enabled') && $setting->getValue('recaptcha_enabled_logins') && $rsp->is_valid)) {
+ if (!$csrfenabled || $csrfenabled && $nocsrf) {
+ 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 {
+ $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: '.$user->getError(), 'TYPE' => 'errormsg');
+ }
} else {
- $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: '. $user->getError(), 'TYPE' => 'errormsg');
+ $_SESSION['POPUP'][] = array('CONTENT' => $csrftoken->getErrorWithDescriptionHTML(), 'TYPE' => 'info');
}
} else {
- $_SESSION['POPUP'][] = array('CONTENT' => $csrftoken->getErrorWithDescriptionHTML(), 'TYPE' => 'info');
+ $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again.', 'TYPE' => 'errormsg');
}
}
// csrf token
diff --git a/public/include/pages/password/change.inc.php b/public/include/pages/password/change.inc.php
index 2c3e08a0..d99ac744 100644
--- a/public/include/pages/password/change.inc.php
+++ b/public/include/pages/password/change.inc.php
@@ -8,7 +8,7 @@ if (!defined('SECURITY'))
$csrfenabled = ($config['csrf']['enabled'] && !in_array('passreset', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
// we have to use editaccount token because this that's where we'll get pushed here from
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'editaccount') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'editaccount', @$_POST['ctoken'])) ? 1 : 0;
}
if (!$csrfenabled || $csrfenabled && $nocsrf) {
diff --git a/public/include/pages/password/reset.inc.php b/public/include/pages/password/reset.inc.php
index a32ce104..4e9c18b2 100644
--- a/public/include/pages/password/reset.inc.php
+++ b/public/include/pages/password/reset.inc.php
@@ -6,7 +6,7 @@ if (!defined('SECURITY')) die('Hacking attempt');
// csrf stuff
$csrfenabled = ($config['csrf']['enabled'] && !in_array('passreset', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'resetpass') == @$_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'resetpass', @$_POST['ctoken'])) ? 1 : 0;
}
// Process password reset request
diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php
index 2cdf762a..ef68fe5f 100644
--- a/public/include/pages/register/register.inc.php
+++ b/public/include/pages/register/register.inc.php
@@ -20,7 +20,7 @@ if ($setting->getValue('recaptcha_enabled') && $setting->getValue('recaptcha_ena
// csrf if enabled
$csrfenabled = ($config['csrf']['enabled'] && !in_array('register', $config['csrf']['disabled_forms'])) ? 1 : 0;
if ($csrfenabled) {
- $nocsrf = ($csrftoken->getBasic($user->getCurrentIP(), 'register') == $_POST['ctoken']) ? 1 : 0;
+ $nocsrf = ($csrftoken->checkBasic($user->getCurrentIP(), 'register', @$_POST['ctoken'])) ? 1 : 0;
}
if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) {