Moved csrftoken stuff into a class

added getCurrentIP method to user class
added config option for sitewide csrf protection
This commit is contained in:
xisi 2014-01-16 22:31:04 -05:00
parent 19a0945be2
commit a56140ca84
7 changed files with 85 additions and 25 deletions

View File

@ -53,6 +53,7 @@ require_once(CLASS_DIR . '/bitcoinwrapper.class.php');
require_once(CLASS_DIR . '/monitoring.class.php');
require_once(CLASS_DIR . '/notification.class.php');
require_once(CLASS_DIR . '/user.class.php');
require_once(CLASS_DIR . '/csrftoken.class.php');
require_once(CLASS_DIR . '/invitation.class.php');
require_once(CLASS_DIR . '/share.class.php');
require_once(CLASS_DIR . '/worker.class.php');

View File

@ -73,6 +73,9 @@ class Base {
public function setTokenType($tokentype) {
$this->tokentype = $tokentype;
}
public function setCSRFToken($token) {
$this->CSRFToken = $token;
}
public function setShare($share) {
$this->share = $share;
}

View File

@ -0,0 +1,45 @@
<?php
// Make sure we are called from index.php
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; 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 getBasic($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;
$lead = $this->config['csrf']['options']['leadtime'];
if ($lead >= 11) { $lead = 10; }
if ($lead <= 0) { $lead = 3; }
if ($minute == 59 && $second > (60-$lead)) {
$minute = 0;
$fhour = ($hour == 23) ? $hour = 0 : $hour+=1;
}
$seed = $seed.$month.$day.$user.$type.$year.$hour.$minute.$seed;
return $this->getHash($seed);
}
private function getHash($string) {
return hash('sha256', $this->salty.$string.$this->salt);
}
}
$csrftoken = new CSRFToken();
$csrftoken->setDebug($debug);
$csrftoken->setMysql($mysqli);
$csrftoken->setSalt(SALT);
$csrftoken->setSalty(SALTY);
$csrftoken->setMail($mail);
$csrftoken->setUser($user);
$csrftoken->setToken($oToken);
$csrftoken->setConfig($config);
$csrftoken->setErrorCodes($aErrorCodes);
?>

View File

@ -794,26 +794,36 @@ class User extends Base {
}
/**
* 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
* Convenience function to get IP address, no params is the same as REMOTE_ADDR
* @param trustremote bool must be FALSE to checkclient or checkforwarded
* @param checkclient bool check HTTP_CLIENT_IP for a valid ip first
* @param checkforwarded bool check HTTP_X_FORWARDED_FOR for a valid ip first
* @return string IP address
*/
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;
$lead = $this->config['csrf']['options']['leadtime'];
if ($lead >= 11) { $lead = 10; }
if ($lead <= 0) { $lead = 3; }
if ($minute == 59 && $second > (60-$lead)) {
$minute = 0;
$fhour = ($hour == 23) ? $hour = 0 : $hour+=1;
public function getCurrentIP($trustremote=true, $checkclient=false, $checkforwarded=false) {
$client = (isset($_SERVER['HTTP_CLIENT_IP'])) ? $_SERVER['HTTP_CLIENT_IP'] : false;
$fwd = (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : false;
$remote = (isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : @$_SERVER['REMOTE_ADDR'];
// shared internet
if (filter_var($client, FILTER_VALIDATE_IP) && !$trustremote && $checkclient) {
return $client;
} else if (strpos($fwd, ',') !== false && !$trustremote && $checkforwarded) {
// multiple proxies
$ips = explode(',', $fwd);
$path = array();
foreach ($ips as $ip) {
if (filter_var($ip, FILTER_VALIDATE_IP)) {
$path[] = $ip;
}
}
return array_pop($path);
} else if (filter_var($fwd, FILTER_VALIDATE_IP) && !$trustremote && $checkforwarded) {
// single
return $fwd;
} else {
// as usual
return $remote;
}
$seed = $seed.$month.$day.$user.$type.$year.$hour.$minute.$seed;
return $this->getHash($seed);
}
}
@ -822,7 +832,6 @@ $user = new User();
$user->setDebug($debug);
$user->setMysql($mysqli);
$user->setSalt(SALT);
$user->setSalty(SALTY);
$user->setSmarty($smarty);
$user->setConfig($config);
$user->setMail($mail);

View File

@ -135,15 +135,18 @@ $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, 1-10s
* login = Use and check CSRF tokens for the login forms
* sitewide = Require a valid CSRF token for all forms, does not override specific form settings
* leadtime = Length of time in seconds to give as leeway between minute switches
* login = Use and check login-specific CSRF token
*
* Default:
* enabled = true
* sitewide = true
* leadtime = 3
* login = true
*/
$config['csrf']['enabled'] = true;
$config['csrf']['sitewide'] = true;
$config['csrf']['options']['leadtime'] = 3;
$config['csrf']['forms']['login'] = true;

View File

@ -25,7 +25,7 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
// csrf token - update if it's enabled
$token = '';
if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) {
$token = $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login');
$token = $csrftoken->getBasic($user->getCurrentIP(), 'login');
}
// Load news entries for Desktop site and unauthenticated users
$smarty->assign("CONTENT", "default.tpl");

View File

@ -3,7 +3,6 @@
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// ReCaptcha handling if enabled
if ($setting->getValue('recaptcha_enabled') && $setting->getValue('recaptcha_enabled_logins')) {
require_once(INCLUDE_DIR . '/lib/recaptchalib.php');
@ -37,7 +36,7 @@ if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserId($_POST
}
}
if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) {
if ((isset($_POST['ctoken']) && $_POST['ctoken'] !== $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login')) || (!isset($_POST['ctoken']))) {
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;
}
@ -61,7 +60,7 @@ if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserId($_POST
// csrf token - update if it's enabled
$token = '';
if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) {
$token = $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login');
$token = $csrftoken->getBasic($user->getCurrentIP(), 'login');
}
// Load login template