diff --git a/.gitignore b/.gitignore
index 3f61187e..03ef8272 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Local Config
/public/include/config/global.inc.php
+/public/include/config/security.inc.php
# Templates
/public/templates/compile/*.php
@@ -14,7 +15,6 @@
# Test configs
public/include/config/global.inc.scrypt.php
public/include/config/global.inc.sha.php
-exploits/*
# IDE Settings
/.idea/*
diff --git a/cronjobs/shared.inc.php b/cronjobs/shared.inc.php
index ecc3da11..fc0d4c9a 100644
--- a/cronjobs/shared.inc.php
+++ b/cronjobs/shared.inc.php
@@ -18,6 +18,21 @@ limitations under the License.
*/
+define('SECURITY', '*)WT#&YHfd');
+// Whether or not to check SECHASH for validity, still checks if SECURITY defined as before if disabled
+define('SECHASH_CHECK', false);
+
+// Nothing below here to configure, move along...
+
+// change SECHASH every second, we allow up to 3 sec back for slow servers
+if (SECHASH_CHECK) {
+ function fip($tr=0) { return md5(SECURITY.(time()-$tr).SECURITY); }
+ define('SECHASH', fip());
+ function cfip() { return (fip()==SECHASH||fip(1)==SECHASH||fip(2)==SECHASH) ? 1 : 0; }
+} else {
+ function cfip() { return (@defined('SECURITY')) ? 1 : 0; }
+}
+
// MODIFY THIS
// We need to find our include files so set this properly
define("BASEPATH", "../public/");
diff --git a/public/include/admin_checks.php b/public/include/admin_checks.php
new file mode 100644
index 00000000..1a28da32
--- /dev/null
+++ b/public/include/admin_checks.php
@@ -0,0 +1,113 @@
+isAdmin(@$_SESSION['USERDATA']['id'])) {
+
+ if (!include_once(INCLUDE_DIR . '/lib/jsonRPCClient.php')) die('Unable to load libs');
+
+ $notice = array();
+ $enotice = array();
+ $error = array();
+
+ // setup some basic stuff for checking
+ $apache_user = posix_getuid();
+ $apache_user = (function_exists('posix_getpwuid')) ? posix_getpwuid($apache_user) : $apache_user;
+
+ // setup checks
+ // check if memcache isn't available but enabled in config -> error
+ if (!class_exists('Memcached') && $config['memcache']['enabled']) {
+ $error[] = "You have memcache enabled in your config and it's not available. Install the package on your system.";
+ }
+ // if it's not enabled, test it if it exists, if it works -> error tell them to enable, -> otherwise notice it's disabled
+ if (!$config['memcache']['enabled']) {
+ if (PHP_OS == 'WINNT') {
+ require_once(CLASS_DIR . 'memcached.class.php');
+ }
+ if (class_exists('Memcached')) {
+ $memcache_test = @new Memcached();
+ $memcache_test_add = @$memcache_test->addServer($config['memcache']['host'], $config['memcache']['port']);
+ $randmctv = rand(5,10);
+ $memcache_test_set = @$memcache_test->set('test_mpos_setval', $randmctv);
+ $memcache_test_get = @$memcache_test->get('test_mpos_setval');
+ }
+ if (class_exists('Memcached') && $memcache_test_get == $randmctv) {
+ $error[] = "You have memcache disabled in the config and it's available & works! Enable it.";
+ } else {
+ $notice[] = "Memcache is disabled; Almost every linux distro has packages for it, you should be using it if you can.";
+ }
+ }
+ // check if we can write templates/cache and templates/compile -> error
+ if (!is_writable(THEME_DIR.'/cache')) {
+ $error[] = "templates/cache folder is not writable for uid {$apache_user['name']}";
+ }
+ if (!is_writable(THEME_DIR.'/compile')) {
+ $error[] = "templates/compile folder is not writable for uid {$apache_user['name']}";
+ }
+ // check if daemon can connect -> error
+ try {
+ if ($bitcoin->can_connect() !== true) {
+ $error[] = "Unable to connect to coin daemon using provided credentials";
+ }
+ } catch (Exception $e) {
+ }
+ // if coldwallet is not empty, check if the address is valid -> error
+ if (!empty($config['coldwallet']['address'])) {
+ try {
+ if ($bitcoin->can_connect() == true) {
+ $validate_cold_address = $bitcoin->validateaddress($config['coldwallet']['address']);
+ if (!$validate_cold_address['isvalid']) {
+ $error[] = "Your cold wallet address is SET and INVALID";
+ }
+ }
+ } catch (Exception $e) {
+ }
+ }
+ // if database connection fails -> error
+ $db_connect = new mysqli($config['db']['host'], $config['db']['user'], $config['db']['pass'], $config['db']['name'], $config['db']['port']);
+ if (mysqli_connect_errno() || !array_key_exists('client_info', $db_connect)) {
+ $error[] = "Unable to connect to mysql using provided credentials";
+ }
+ if (($config['strict'] || $config['mc_antidos']) && !$config['memcache']['enabled']) {
+ $error[] = "strict or mc_antidos are enabled and memcache is not, memcache is required to use these.";
+ }
+ // poke stratum using gettingstarted details -> enotice
+ $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+ if ($socket !== false) {
+ $address = @gethostbyname($config['gettingstarted']['stratumurl']);
+ $result = @socket_connect($socket, $address, $config['gettingstarted']['stratumport']);
+ if ($result !== 1) {
+ $enotice[] = "We tried to poke your Stratum server using config->gettingstarted details but it didn't respond";
+ }
+ $close = @socket_close($socket);
+ }
+ // security checks
+ // strict not on -> notice
+ if (!$config['strict']) {
+ $notice[] = "strict is disabled - if you have memcache, you should turn this on.";
+ }
+ // salts too short -> notice, salts default -> error
+ if ((strlen(SALT) < 24) || (strlen(SALTY) < 24) || SALT == 'PLEASEMAKEMESOMETHINGRANDOM' || SALTY == 'THISSHOULDALSOBERRAANNDDOOM') {
+ if (SALT == 'PLEASEMAKEMESOMETHINGRANDOM' || SALTY == 'THISSHOULDALSOBERRAANNDDOOM') {
+ $error[] = "You absolutely SHOULD NOT leave your SALT or SALTY default";
+ } else {
+ $notice[] = "SALT or SALTY is too short, they should be more than 24 characters and changing them will require registering again.";
+ }
+ }
+
+ // display the errors
+ foreach ($enotice as $en) {
+ $_SESSION['POPUP'][] = array('CONTENT' => $en, 'TYPE' => 'info');
+ }
+ if (!count($notice) && !count($error)) {
+ $_SESSION['POPUP'][] = array('CONTENT' => 'The config options we checked seem OK', 'TYPE' => 'success');
+ } else {
+ foreach ($notice as $n) {
+ $_SESSION['POPUP'][] = array('CONTENT' => $n, 'TYPE' => 'warning');
+ }
+ foreach ($error as $e) {
+ $_SESSION['POPUP'][] = array('CONTENT' => $e, 'TYPE' => 'errormsg');
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php
index 8fee8e48..5042e09e 100644
--- a/public/include/autoloader.inc.php
+++ b/public/include/autoloader.inc.php
@@ -71,4 +71,8 @@ require_once(INCLUDE_DIR . '/lib/scrypt.php');
// Include our versions
require_once(INCLUDE_DIR . '/version.inc.php');
+if ($user->isAdmin(@$_SESSION['USERDATA']['id'])) {
+ //include_once(INCLUDE_DIR . '/admin_checks.inc.php');
+}
+
?>
diff --git a/public/include/classes/memcache_ad.class.php b/public/include/classes/memcache_ad.class.php
index 57ef2ca7..25b48cf5 100644
--- a/public/include/classes/memcache_ad.class.php
+++ b/public/include/classes/memcache_ad.class.php
@@ -4,66 +4,119 @@ $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
class MemcacheAntiDos
{
public $cache;
- public static $key = 'mcad_';
- public static $request_model = array(
- 'ident' => '',
- 'last_hit' => 0,
- 'last_flush' => 0,
- 'hits_since_flush' => 0
- );
public $rate_limit_this_request = false;
- public function __construct($config, &$memcache, $userORip, $request, $mcSettings) {
+ public $rate_limit_api_request = false;
+ public $rate_limit_site_request = false;
+ public function __construct($config, &$memcache, $userORip, $request='', $mcSettings) {
$this->cache = $memcache;
// set our config options
- $per_page = $config['per_page'];
- $flush_sec = $config['flush_seconds'];
- $rate_limit = $config['rate_limit'];
+ $per_page = '';
+ $flush_sec_api = $config['flush_seconds_api'];
+ $rate_limit_api = $config['rate_limit_api'];
+ $flush_sec_site = $config['flush_seconds_site'];
+ $rate_limit_site = $config['rate_limit_site'];
+ $ajax_add = $config['ajax_hits_additive'];
unset($config);
// prep stuff we need to check this request
- $key_md5 = substr(md5($userORip), 0, 4);
- $request_md5 = substr(md5($request), 0, 4);
- $request_key = $mcSettings['keyprefix'].self::$key.$key_md5."_".$request_md5."_".$per_page;
- $request_data = $this->cache->get($request_key);
+ $key_md5 = md5($mcSettings['keyprefix'].$userORip);
+ $request_data = $this->cache->get($key_md5);
$now = time();
+ $max_req_flush = max(array($flush_sec_api,$flush_sec_site));
// check the request
if (is_array($request_data)) {
// this request key already exists, update it
- $request_data['ident'] = $key_md5;
- $request_data['last_hit'] = $now;
- $request_data['hits_since_flush'] += 1;
+ $request_data['la'] = $now;
+ if ($request == 'api') {
+ $request_data['ha'] += 1;
+ if ($ajax_add) {
+ $request_data['hn'] += 1;
+ }
+ } else {
+ $request_data['hn'] += 1;
+ }
// not rate limited yet, update the rest of the object
- if ($request_data['hits_since_flush'] < $rate_limit) {
- if (($request_data['last_flush'] + $flush_sec) <= $now || ($request_data['last_hit'] + $flush_sec) <= $now) {
- // needs to be flushed
- $request_data['hits_since_flush'] = 0;
- $request_data['last_hit'] = 0;
- $request_data['last_flush'] = $now;
- // update the object
- $this->cache->set($request_key, $request_data, $flush_sec);
- $this->rate_limit_this_request = false;
+ if (($request_data['hn'] < $rate_limit_site) && ($request_data['ha'] < $rate_limit_api)) {
+
+ if (((($request_data['hnl'] + $flush_sec_site) <= $now) || ($request_data['hal'] + $flush_sec_api) <= $now) || (($request_data['la'] + $max_req_flush) <= $now)) {
+ // needs to be flushed & updated
+ $new = $this->getRequestBase();
+ $new['key'] = $key_md5;
+ $new['sid'] = session_id();
+ $new['ua'] = md5($_SERVER['HTTP_USER_AGENT']);
+ $new['ip'] = $key_md5;
+ $new['la'] = $now;
+ $new['hal'] = ((($request_data['hal'] + $flush_sec_api) <= $now)) ? $now : 1;
+ $new['hnl'] = ((($request_data['hnl'] + $flush_sec_site) <= $now)) ? $now : 1;
+ $this->cache->set($key_md5, $new, $max_req_flush);
+ $this->rate_limit_api_request = ($request_data['ha'] >= $rate_limit_api) ? true : false;
+ $this->rate_limit_site_request = ($request_data['hn'] >= $rate_limit_site) ? true : false;
+ //$this->rate_limit_this_request = false;
} else {
// no flush, just update
- $this->cache->set($request_key, $request_data, $flush_sec);
- $this->rate_limit_this_request = false;
+ $new = $this->getRequestBase();
+ $new['key'] = $key_md5;
+ $new['sid'] = session_id();
+ $new['ua'] = md5($_SERVER['HTTP_USER_AGENT']);
+ $new['ip'] = $key_md5;
+ $new['la'] = time();
+ $new['ha'] = $request_data['ha'];
+ $new['hal'] = $request_data['hal'];
+ $new['hn'] = $request_data['hn'];
+ $new['hnl'] = $request_data['hnl'];
+ $this->cache->set($key_md5, $new, $max_req_flush);
+ //$this->rate_limit_this_request = false;
+ $this->rate_limit_api_request = ($request_data['ha'] >= $rate_limit_api) ? true : false;
+ $this->rate_limit_site_request = ($request_data['hn'] >= $rate_limit_site) ? true : false;
}
} else {
// too many hits, we should rate limit this
- $this->rate_limit_this_request = true;
+ //$this->rate_limit_this_request = true;
+ $this->rate_limit_api_request = ($request_data['ha'] >= $rate_limit_api) ? true : false;
+ $this->rate_limit_site_request = ($request_data['hn'] >= $rate_limit_site) ? true : false;
}
} else {
// doesn't exist for this request_key, create one
- $new_data = self::$request_model;
- $new_data['ident'] = $key_md5;
- $new_data['last_hit'] = time();
- $new_data['hits_since_flush'] = 1;
- $new_data['last_flush'] = $now;
- $this->cache->set($request_key, $new_data, $flush_sec);
+ $new = $this->getRequestBase();
+ $new['key'] = $key_md5;
+ $new['sid'] = session_id();
+ $new['ua'] = md5($_SERVER['HTTP_USER_AGENT']);
+ $new['ip'] = $key_md5;
+ $new['la'] = time();
+ if ($request == 'api') {
+ $new['ha'] += 1;
+ if ($ajax_add) {
+ $new['hn'] += 1;
+ }
+ } else {
+ $new['hn'] += 1;
+ }
+ $this->cache->set($key_md5, $new, $max_req_flush);
$this->rate_limit_this_request = false;
}
}
+ public function getRequestBase() {
+ $new = array(
+ 'key' => '',
+ 'sid' => '',
+ 'ua' => '',
+ 'ip' => '',
+ 'la' => 0,
+ 'hn' => 0,
+ 'hnl' => 0,
+ 'ha' => 0,
+ 'hal' => 0
+ );
+ return $new;
+ }
public function rateLimitRequest() {
return $this->rate_limit_this_request;
}
+ public function rateLimitSite() {
+ return $this->rate_limit_site_request;
+ }
+ public function rateLimitAPI() {
+ return $this->rate_limit_api_request;
+ }
}
?>
\ No newline at end of file
diff --git a/public/include/classes/strict.class.php b/public/include/classes/strict.class.php
index db23cd13..9a535c54 100644
--- a/public/include/classes/strict.class.php
+++ b/public/include/classes/strict.class.php
@@ -1,108 +1,140 @@
bind_address !== $this->server_http_host) {
- return false;
- } else {
- return true;
- }
+ public function session_delete_key($key) {
+ $read = $this->memcache->delete($key);
}
-
- public function verify_client($ip) {
- if ($this->started && $this->memcache_handle !== null && $this->verify_server()) {
- $read_client = $this->memcache_handle->get(md5((string)$ip));
- if (is_array($read_client) && $read_client[0] == session_id()) {
- return true;
- } else {
- return false;
+ private $validation_misses = 0;
+ private $initial_ua;
+ public function create_or_update_client($client, $force=false, $login=false) {
+ $read = $this->memcache->get($client['key']);
+ // this needs to be available later
+ $update = array('key' => '','sid' => '','ua' => '','ip' => '','la' => 0,'hn' => 0,'hnl' => 0,'ha' => 0,'hal' => 0);
+ $update['sid'] = $client['sid'];
+ $update['ua'] = md5($this->initial_ua);
+ $update['ip'] = $client['ip'];
+ $update['la'] = time();
+ $update['key'] = md5($this->memcache_key.$client['ip']);
+ $validation_misses = 0;
+ if ($read !== false) {
+ $read_model = array('key' => '','sid' => '','ua' => '','ip' => '','la' => 0,'hn' => 0,'hnl' => 0,'ha' => 0,'hal' => 0);
+ $read_model['sid'] = @$read['sid'];
+ $read_model['ip'] = @$read['ip'];
+ $read_model['ua'] = @$read['ua'];
+ $read_model['la'] = @$read['la'];
+ $read_model['key'] = md5($this->memcache_key.$read['ip']);
+ // key already exists, update
+ if ($this->validate_client) {
+ if ($this->verify_client($read_model, $update, $login)) {
+ $update_client = $this->memcache->set($update['key'], $update);
+ }
}
} else {
- return false;
- }
- }
-
- public function update_client($ip) {
- if ($this->started && $this->memcache_handle !== null && $this->verify_client($ip)) {
- $this->memcache_handle->set(md5((string)$ip), array($this->current_session_id, time()));
- }
- }
-
- public function set_cookie($ip) {
- if ($this->started && $this->memcache_handle !== null && $this->verify_server() && $this->verify_client($ip)) {
- @setcookie(session_name(), session_id(), $this->config_dura, $this->config_path, $this->config_domain, $this->config_secure, $this->config_httponly);
- }
- }
-
- public function destroy_session($ip) {
- if ($this->started && $this->verify_server() && $this->verify_client($ip)) {
- $this->memcache_handle->delete(md5((string)$ip));
- if (ini_get('session.use_cookies')) {
- setcookie(session_name(), '', time() - 42000, $config_path, $config_domain, $config_secure, $config_httponly);
- }
- session_destroy();
- session_regenerate_id(true);
- }
- }
-
- public function create_session($ip) {
- if (!$this->verify_server()) {
- return false;
- } else {
- $session_start = @session_start();
- if (!$session_start) {
- session_destroy();
- session_regenerate_id(true);
- session_start();
- $this->update_client($ip);
- $this->started = true;
- $this->current_session_id = session_id();
- $this->set_cookie($ip);
- return true;
- } else {
- $this->update_client($ip);
- $this->started = true;
- $this->current_session_id = session_id();
- $this->set_cookie($ip);
- return true;
+ $update_client = $this->memcache->set($client['key'], $client);
+ if ($force && $login) {
+ $update_client = $this->memcache->set($update['key'], $update);
}
}
}
-
- public function __construct($config, &$memcache, $server_host) {
- $this->config_dura = $config['cookie']['duration'];
- $this->config_path = $config['cookie']['path'];
- $this->config_domain = $config['cookie']['domain'];
- $this->config_secure = $config['cookie']['secure'];
- $this->config_httponly = $config['cookie']['httponly'];
- if ($config['strict__enforce_ssl']) $config['strict__bind_protocol'] = 'https';
- $this->bind_address = $config['strict__bind_protocol']."://".$config['strict__bind_host'].":".$config['strict__bind_port'];
- $this->server_http_host = $config['strict__bind_protocol']."://".$_SERVER['HTTP_HOST'].":".$config['strict__bind_port'];
- $this->memcache_handle = $memcache;
- unset($config);
- $this->set_cookie_params((time()+$this->config_dura), $this->config_path, $this->config_domain, $this->config_secure, $this->config_httponly);
+ public function verify_client($client_model, $data, $login=false) {
+ $fails = 0;
+ $fails += ((count($client_model)) !== (count($data))) ? 1 : 0;
+ $fails += ($client_model['ua'] !== $data['ua']) ? 1 : 0;
+ $fails += ($client_model['ip'] !== $data['ip']) ? 1 : 0;
+ $now = time();
+ $this->validation_misses = $fails;
+ if ($fails > $this->validate_client_num && $login == false) {
+ // something changed
+ $port = ($_SERVER["SERVER_PORT"] == "80" || $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
+ $location = (@$_SERVER['HTTPS'] == "on") ? 'https://' : 'http://';
+ $location .= $_SERVER['SERVER_NAME'] . $port . $_SERVER['SCRIPT_NAME'];
+ $this->session_delete_key($client_model['key']);
+ $this->session_delete_key($data['key']);
+ @session_start();
+ @session_regenerate_id(true);
+ $_SESSION = null;
+ $_SESSION['POPUP'][] = array('CONTENT' => "Session revoked due to a change in your client. You may have a plugin messing with your useragent, or your IP address may have changed.", 'TYPE' => 'warning');
+ $location.= '?page=login';
+ if (!headers_sent()) exit(header('Location: ' . $location));
+ exit('');
+ }
+ return ($fails > 0) ? false : true;
+ }
+ public function read_if_client_exists($client_key) {
+ if ($this->memcache !== null) {
+ $exists = $this->memcache->get($client_key);
+ }
+ return ($exists !== null) ? $exists : false;
+ }
+ public function regen_session_id() {
+ $sidbefore = @session_id();
+ @session_regenerate_id(true);
+ $sid = session_id();
+ return $sid;
+ }
+ public function __construct($config, &$memcache) {
+ $this->initial_ua = $_SERVER['HTTP_USER_AGENT'];
+ $this->memcache = $memcache;
+ $this->memcache_key = $config['memcache']['keyprefix'];
+ if ($config['strict__verify_client']) {
+ $this->validate_client = true;
+ $this->validate_client_ip = $config['strict__verify_client_ip'];
+ $this->validate_client_ua = $config['strict__verify_client_useragent'];
+ $this->validate_client_sid = $config['strict__verify_client_sessionid'];
+ $this->validate_client_num = 0;
+ if ($config['strict__verify_server']) {
+ $proto = (@$_SERVER['HTTPS'] == "on") ? 'https' : 'http';
+ $location = $proto."://".$_SERVER['SERVER_NAME'] . $_SERVER['SERVER_PORT'];
+ if ($config['strict__verify_server']) {
+ if ($config['strict__bind_protocol']."://".$config['strict__bind_host'].$config['strict__bind_port'] !== $location) {
+ return false;
+ }
+ }
+ }
+ $client = array('key' => '','sid' => '','ua' => '','ip' => '','la' => 0,'hn' => 0,'hnl' => 0,'ha' => 0,'hal' => 0);
+ $client['ua'] = md5($_SERVER['HTTP_USER_AGENT']);
+ $client['ip'] = md5($_SERVER['REMOTE_ADDR']);
+ $client['la'] = time();
+ $client['key'] = md5($this->memcache_key.$client['ip']);
+ $read = $this->read_if_client_exists($client['key']);
+ }
+ session_set_cookie_params((time()+$config['cookie']['duration']), $config['cookie']['path'], $config['cookie']['domain'], false, true);
+ $session_start = @session_start();
+ $client['sid'] = session_id();
+ $valid_session_id = $this->valid_session_id($client['sid']);
+ if (!$valid_session_id || !$session_start) {
+ @session_destroy();
+ $client['sid'] = $this->regen_session_id();
+ session_start();
+ }
+ if ($read !== null) {
+ // client exists, verify
+ $this->create_or_update_client($client, true, false);
+
+ } else {
+ // doesn't exist
+ $this->create_or_update_client($client, true, true);
+ }
+ @setcookie(session_name(), $client['sid'], (time()+$config['cookie']['duration']), $config['cookie']['path'], $config['cookie']['domain'], false, true);
+ // post changes validate
+ if ($this->validate_client) {
+ $read_post = $this->read_if_client_exists($client['key']);
+ if ($read_post !== null) {
+ $this->verify_client($client, $read_post, true);
+ }
+ }
}
}
@@ -115,26 +147,28 @@ class mysqli_strict extends mysqli {
$acopy = $args;
$nargs = count($args);
for($i=1;$i<$nargs;$i++) {
- $pos = substr($paramTypes, ($i-1), 1);
+ $ipos = ($i-1);
+ $pos = substr($paramTypes, $ipos, 1);
switch ($pos) {
case 's':
$return_str = filter_var($acopy[$i], FILTER_VALIDATE_STRING, FILTER_NULL_ON_FAILURE);
- return ($return_str !== null) ? (string)$return_str : false;
+ $acopy[$i] = ($return_str !== null) ? (string)$return_str : null;
break;
case 'i':
$return_int = filter_var($acopy[$i], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
- return ($return_int !== null) ? (int)$return_int : false;
+ $acopy[$i] = ($return_int !== null) ? (int)$return_int : null;
break;
case 'd':
$return_dbl = filter_var($acopy[$i], FILTER_VALIDATE_FLOAT, FILTER_NULL_ON_FAILURE);
- return ($return_dbl !== null) ? (float)$return_dbl : false;
+ $acopy[$i] = ($return_dbl !== null) ? (float)$return_dbl : null;
break;
case 'b':
$return_bool = filter_var($acopy[$i], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
- return ($return_bool !== null) ? (bool)$return_bool : false;
+ $acopy[$i] = ($return_bool !== null) ? (bool)$return_bool : null;
break;
}
}
+ return (in_array(null, $acopy));
}
}
}
diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php
index d7385b8a..1c0feee0 100644
--- a/public/include/classes/user.class.php
+++ b/public/include/classes/user.class.php
@@ -494,13 +494,10 @@ class User extends Base {
$this->debug->append("STA " . __METHOD__, 4);
$this->debug->append("Log in user to _SESSION", 2);
if ($this->config['strict']) {
- if ($this->session->verify_server()) {
- if ($this->session->create_session($_SERVER['REMOTE_ADDR'])) {
- $this->session->update_client($_SERVER['REMOTE_ADDR']);
- $_SESSION['AUTHENTICATED'] = '1';
- $_SESSION['USERDATA'] = $this->user;
- }
- }
+ session_regenerate_id(true);
+ $_SESSION['AUTHENTICATED'] = '1';
+ // $this->user from checkUserPassword
+ $_SESSION['USERDATA'] = $this->user;
} else {
session_regenerate_id(true);
$_SESSION['AUTHENTICATED'] = '1';
@@ -537,10 +534,11 @@ class User extends Base {
session_destroy();
// Enforce generation of a new Session ID and delete the old
session_regenerate_id(true);
+
// Enforce a page reload and point towards login with referrer included, if supplied
$port = ($_SERVER["SERVER_PORT"] == "80" || $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
$pushto = $_SERVER['SCRIPT_NAME'].'?page=login';
- $location = @$_SERVER['HTTPS'] ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $pushto : 'http://' . $_SERVER['SERVER_NAME'] . $port . $pushto;
+ $location = (@$_SERVER['HTTPS'] == 'on') ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $pushto : 'http://' . $_SERVER['SERVER_NAME'] . $port . $pushto;
// if (!headers_sent()) header('Location: ' . $location);
exit('');
}
@@ -804,7 +802,7 @@ class User extends Base {
* @param none
* @return bool
**/
- public function isAuthenticated($logout=true) {
+public function isAuthenticated($logout=true) {
$this->debug->append("STA " . __METHOD__, 4);
if (@$_SESSION['AUTHENTICATED'] == true &&
!$this->isLocked($_SESSION['USERDATA']['id']) &&
diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php
index 63bee3a6..63c44b83 100644
--- a/public/include/config/global.inc.dist.php
+++ b/public/include/config/global.inc.dist.php
@@ -1,25 +1,6 @@
Memcache configuration
- * Check -> Memcache anti resource-dos
- *
- * Runs a FILTER_VALIDATE_*TYPE on every parameter of bind_param
- * Verifies server vs. bound protocol/host/port set below
- * Enables memcache rate limiting of requests
- * Verifies client when creating/resuming from a session
- */
-$config['strict'] = true;
-$config['strict__enforce_ssl'] = false;
-$config['strict__bind_protocol'] = 'http';
-$config['strict__bind_host'] = 'localhost';
-$config['strict__bind_port'] = 80;
-
/**
* Do not edit this unless you have confirmed that your config has been updated!
* This is used in the version check to ensure you run the latest version of the configuration file.
@@ -27,6 +8,11 @@ $config['strict__bind_port'] = 80;
**/
$config['version'] = '0.0.7';
+/**
+ * Unless you disable this, we'll do a quick check on your config first.
+ */
+$config['skip_config_tests'] = false;
+
// Our include directory for additional features
define('INCLUDE_DIR', BASEPATH . 'include');
@@ -119,64 +105,6 @@ $config['coldwallet']['address'] = '';
$config['coldwallet']['reserve'] = 50;
$config['coldwallet']['threshold'] = 5;
-/**
- * E-mail confirmations for user actions
- *
- * Explanation:
- * To increase security for users, account detail changes can require
- * an e-mail confirmation prior to performing certain actions.
- *
- * Options:
- * enabled : Whether or not to require e-mail confirmations
- * details : Require confirmation to change account details
- * withdraw : Require confirmation to manually withdraw/payout
- * changepw : Require confirmation to change password
- *
- * Default:
- * enabled = true
- * details = true
- * withdraw = true
- * changepw = true
- */
-$config['twofactor']['enabled'] = true;
-$config['twofactor']['options']['details'] = true;
-$config['twofactor']['options']['withdraw'] = true;
-$config['twofactor']['options']['changepw'] = true;
-
-/**
- * CSRF protection
- *
- * 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 a
- * form is submitted.
- *
- * Options:
- * enabled = Whether or not we will generate & check for valid CSRF tokens
- * Default:
- * enabled = true
- */
-$config['csrf']['enabled'] = true;
-
-/**
- * Lock account after maximum failed logins
- *
- * Explanation:
- * To avoid accounts being hacked by brute force attacks,
- * set a maximum amount of failed login or pin entry attempts before locking
- * the account. They will need to contact site support to re-enable the account.
- *
- * This also applies for invalid PIN entries, which is covered by the pin option.
- *
- * Workers are not affected by this lockout, mining will continue as usual.
- *
- * Default:
- * login = 3
- * pin = 3
- **/
-$config['maxfailed']['login'] = 3;
-$config['maxfailed']['pin'] = 3;
-
/**
* Getting Started Config
*
diff --git a/public/include/config/security.inc.dist.php b/public/include/config/security.inc.dist.php
new file mode 100644
index 00000000..66612efe
--- /dev/null
+++ b/public/include/config/security.inc.dist.php
@@ -0,0 +1,136 @@
+ Memcache configuration
+ * Check -> Memcache anti resource-dos
+ *
+ * Options Default Explanation
+ * ------- + ------- + -----------
+ * strict : true : Whether or not to use strict mode
+ * __https_only : false : Requires/pushes to https
+ * __mysql_filter : true : Uses a mysqli shim to use php filters on all incoming data
+ * __verify_client : true : Verifies the client using specified settings
+ * __verify_client_ip : true : If the client request suddenly switches IP, trigger a failure
+ * __verify_client_useragent : true : If the client request suddenly switches Useragent, trigger a failure
+ * __verify_client_sessionid : true : If the client request suddenly switches SessionID, trigger a failure
+ * __verify_client_fails : 0 : Maximum number of client-side inconsistencies to accept before revoking sessions
+ * __verify_server : false : Verifies the server is valid for this request
+ * __bind_protocol : https : Server validate protocol; http or https
+ * __bind_host : '' : Server validate host; ie. your domain or subdomain
+ * __bind_port : 443 : Server validate port; 80 / 443 / something else
+ **/
+$config['strict'] = true;
+$config['strict__https_only'] = false;
+$config['strict__mysql_filter'] = true;
+$config['strict__verify_client'] = true;
+$config['strict__verify_client_ip'] = true;
+$config['strict__verify_client_useragent'] = true;
+$config['strict__verify_client_sessionid'] = true;
+$config['strict__verify_client_fails'] = 0;
+$config['strict__verify_server'] = false;
+$config['strict__bind_protocol'] = 'https';
+$config['strict__bind_host'] = '';
+$config['strict__bind_port'] = 443;
+
+/**
+ * Memcache anti resource-dos protection / request rate limiting
+ *
+ * Explanation:
+ * Because bots/angry users can just fire away at pages or f5 us to death, we can attempt to rate limit requests
+ * using memcache - now shares data with session manager.
+ *
+ * Options:
+ * enabled = Whether or not we will try to rate limit requests
+ * protect_ajax = If enabled, we will also watch the ajax calls for rate limiting and kill bad requests
+ * ajax_hits_additive = If enabled, ajax hits will count towards the site counter as well as the ajax counter
+ * flush_seconds_api = Number of seconds between each flush of user/ajax counter
+ * rate_limit_api = Number of api requests allowed per flush_seconds_api
+ * flush_seconds_site = Number of seconds between each flush of user/site counter
+ * rate_limit_site = Number of site requests allowed per flush_seconds_site
+ * ignore_admins = Ignores the rate limit for admins
+ * error_push_page = Page/action array to push users to a specific page, look in the URL!
+ * Empty = 'You are sending too many requests too fast!' on a blank page
+ * Default:
+ * enabled = true
+ * protect_ajax = true
+ * ajax_hits_additive = false
+ * flush_seconds_api = 60
+ * rate_limit_api = 20
+ * flush_seconds_site = 60
+ * rate_limit_site = 30
+ * ignore_admins = true
+ * error_push_page = array('page' => 'error', 'action' => 'ratelimit');
+ */
+$config['mc_antidos']['enabled'] = true;
+$config['mc_antidos']['protect_ajax'] = true;
+$config['mc_antidos']['ajax_hits_additive'] = false;
+$config['mc_antidos']['flush_seconds_api'] = 60;
+$config['mc_antidos']['rate_limit_api'] = 20;
+$config['mc_antidos']['flush_seconds_site'] = 60;
+$config['mc_antidos']['rate_limit_site'] = 30;
+$config['mc_antidos']['ignore_admins'] = true;
+$config['mc_antidos']['error_push_page'] = array('page' => 'error', 'action' => 'ratelimit');
+
+/**
+ * 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 a
+ * form is submitted.
+ *
+ * Options:
+ * enabled = Whether or not we will generate/check for valid CSRF tokens
+ * Default:
+ * enabled = true
+ */
+$config['csrf']['enabled'] = true;
+
+/**
+ * E-mail confirmations for user actions
+ *
+ * Explanation:
+ * To increase security for users, account detail changes can require
+ * an e-mail confirmation prior to performing certain actions.
+ *
+ * Options:
+ * enabled : Whether or not to require e-mail confirmations
+ * details : Require confirmation to change account details
+ * withdraw : Require confirmation to manually withdraw/payout
+ * changepw : Require confirmation to change password
+ *
+ * Default:
+ * enabled = true
+ * details = true
+ * withdraw = true
+ * changepw = true
+ */
+$config['twofactor']['enabled'] = true;
+$config['twofactor']['options']['details'] = true;
+$config['twofactor']['options']['withdraw'] = true;
+$config['twofactor']['options']['changepw'] = true;
+
+/**
+ * Lock account after maximum failed logins
+ *
+ * Explanation:
+ * To avoid accounts being hacked by brute force attacks,
+ * set a maximum amount of failed login or pin entry attempts before locking
+ * the account. They will need to contact site support to re-enable the account.
+ *
+ * This also applies for invalid PIN entries, which is covered by the pin option.
+ *
+ * Workers are not affected by this lockout, mining will continue as usual.
+ *
+ * Default:
+ * login = 3
+ * pin = 3
+ **/
+$config['maxfailed']['login'] = 3;
+$config['maxfailed']['pin'] = 3;
+
+?>
\ No newline at end of file
diff --git a/public/include/database.inc.php b/public/include/database.inc.php
index 870e6dfa..1146e239 100644
--- a/public/include/database.inc.php
+++ b/public/include/database.inc.php
@@ -2,7 +2,7 @@
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
// Instantiate class, we are using mysqlng
-if ($config['strict']) {
+if ($config['strict'] && $config['strict__mysql_filter']) {
$mysqli = new mysqli_strict($config['db']['host'], $config['db']['user'], $config['db']['pass'], $config['db']['name'], $config['db']['port']);
} else {
$mysqli = new mysqli($config['db']['host'], $config['db']['user'], $config['db']['pass'], $config['db']['name'], $config['db']['port']);
diff --git a/public/include/pages/login.inc.php b/public/include/pages/login.inc.php
index ff3b36ae..84964672 100644
--- a/public/include/pages/login.inc.php
+++ b/public/include/pages/login.inc.php
@@ -24,10 +24,22 @@ if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserIdByEmail
// Check if recaptcha is enabled, process form data if valid
if (!$setting->getValue('recaptcha_enabled') || !$setting->getValue('recaptcha_enabled_logins') || ($setting->getValue('recaptcha_enabled') && $setting->getValue('recaptcha_enabled_logins') && $rsp->is_valid)) {
if (!$config['csrf']['enabled'] || $config['csrf']['enabled'] && $csrftoken->valid) {
+ // check if login is correct
if ($user->checkLogin(@$_POST['username'], @$_POST['password']) ) {
- $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
- $location = @$_SERVER['HTTPS'] ? 'https://' : 'http://';
- $location .= $_SERVER['SERVER_NAME'] . $port . $_SERVER['SCRIPT_NAME'] . '?page=dashboard';
+ $port = ($_SERVER["SERVER_PORT"] == "80" || $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
+ $location = (@$_SERVER['HTTPS'] == "on") ? 'https://' : 'http://';
+ $location .= $_SERVER['SERVER_NAME'] . $port . $_SERVER['SCRIPT_NAME'];
+ if ($config['strict']) {
+ $update = array('key' => '','sid' => '','ua' => '','ip' => '','la' => 0,'hn' => 0,'hnl' => 0,'ha' => 0,'hal' => 0);
+ $session->regen_session_id();
+ $update['sid'] = session_id();
+ $update['ua'] = md5($_SERVER['HTTP_USER_AGENT']);
+ $update['ip'] = md5($_SERVER['REMOTE_ADDR']);
+ $update['la'] = time();
+ $update['key'] = md5($update['ip']);
+ $session->create_or_update_client($update, true, true);
+ $location.= '?page=dashboard';
+ }
if (!headers_sent()) header('Location: ' . $location);
exit('');
} else {
@@ -40,7 +52,7 @@ if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserIdByEmail
$_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again.', 'TYPE' => 'errormsg');
}
}
-
// Load login template
$smarty->assign('CONTENT', 'default.tpl');
+
?>
diff --git a/public/include/pages/logout.inc.php b/public/include/pages/logout.inc.php
index 9b6e12a6..c15f350a 100644
--- a/public/include/pages/logout.inc.php
+++ b/public/include/pages/logout.inc.php
@@ -1,7 +1,18 @@
logoutUser();
+if ($config['strict']) {
+ $user->logoutUser();
+ $update = $session::$client_model;
+ $update['sid'] = session_id();
+ $update['ua'] = $_SERVER['HTTP_USER_AGENT'];
+ $update['ip'] = $_SERVER['REMOTE_ADDR'];
+ $update['la'] = time();
+ $update['key'] = md5($update['ua'].$update['ip']);
+ $session->create_or_update_client($update, true);
+} else {
+ $user->logoutUser();
+}
$smarty->assign("CONTENT", "default.tpl");
?>
diff --git a/public/include/version.inc.php b/public/include/version.inc.php
index 6206c0a2..d1db6f62 100644
--- a/public/include/version.inc.php
+++ b/public/include/version.inc.php
@@ -1,7 +1,7 @@
addServer($config['memcache']['host'], $config['memcache']['port']);
}
-if ($config['strict']) {
+if ($config['memcache']['enabled'] && $config['strict'] || $config['mc_antidos']['enabled']) {
require_once(CLASS_DIR . '/memcache_ad.class.php');
- $session = new SessionManager($config, $memcache, $_SERVER['HTTP_HOST']);
- $user->setSessionManager($session);
- if ($session->verify_server()) {
- $session->create_session($_SERVER['REMOTE_ADDR']);
- if ($session->verify_client($_SERVER['REMOTE_ADDR'])) {
- $session->update_client($_SERVER['REMOTE_ADDR']);
- }
+}
+
+if ($config['memcache']['enabled'] && $config['strict']) {
+ $session = new strict_session($config, $memcache);
+ if ($config['strict__verify_server'] && !$session) {
+ // server not verified, session manager will kill the client verification failures
+ exit(header('HTTP/1.1 401 Unauthorized'));
}
} else {
- session_set_cookie_params(time()+$config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']);
$session_start = @session_start();
+ session_set_cookie_params(time()+$config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']);
if (!$session_start) {
session_destroy();
session_regenerate_id(true);
@@ -79,11 +83,12 @@ if ($config['strict']) {
}
@setcookie(session_name(), session_id(), time()+$config['cookie']['duration'], $config['cookie']['path'], $config['cookie']['domain'], $config['cookie']['secure'], $config['cookie']['httponly']);
}
+
// Rate limiting
-if ($config['memcache']['enabled'] && $config['mc_antidos']['enabled'] || $config['strict']) {
+if ($config['memcache']['enabled'] && ($config['mc_antidos']['enabled'] || $config['strict'])) {
$skip_check = false;
- $per_page = ($config['mc_antidos']['per_page']) ? $_SERVER['QUERY_STRING'] : '';
// if this is an api call we need to be careful not to time them out for those calls separately
+ $per_page = '';
$ajax_calls = array(
array('api', 'getuserbalance'),
array('api', 'getnavbardata'),
@@ -96,9 +101,7 @@ if ($config['memcache']['enabled'] && $config['mc_antidos']['enabled'] || $confi
}
$is_ajax_call = ($iac > 0) ? true : false;
if ($is_ajax_call && $config['mc_antidos']['protect_ajax']) {
- // we set this to navbar on purpose - if they screw with the REQUEST by adding more
- // params it still gets added under navbar so multiple requests will still get capped
- $per_page = 'navbar';
+ $per_page = 'api';
} else if ($is_ajax_call && !$config['mc_antidos']['protect_ajax']) {
// protect isn't on, we'll ignore it
$skip_check = true;
@@ -107,9 +110,13 @@ if ($config['memcache']['enabled'] && $config['mc_antidos']['enabled'] || $confi
}
if (!$skip_check) {
$mcad = new MemcacheAntiDos($config['mc_antidos'], $memcache, $_SERVER['REMOTE_ADDR'], $per_page, $config['memcache']);
- $rate_limit_reached = $mcad->rateLimitRequest();
+ $rate_limit_reached_site = $mcad->rateLimitSite();
+ $rate_limit_reached_api = $mcad->rateLimitAPI();
+ if ($rate_limit_reached_api && $is_ajax_call && $config['mc_antidos']['protect_ajax']) {
+ exit(header('HTTP/1.1 401 Unauthorized'));
+ }
$error_page = $config['mc_antidos']['error_push_page'];
- if ($rate_limit_reached == true) {
+ if ($rate_limit_reached_site == true) {
if (!is_array($error_page) || count($error_page) < 1 || (empty($error_page['page']) && empty($error_page['action']))) {
die("You are sending too many requests too fast!");
} else {
@@ -120,6 +127,11 @@ if ($config['memcache']['enabled'] && $config['mc_antidos']['enabled'] || $confi
}
}
+// Quick config check
+if (@$_SESSION['USERDATA']['is_admin'] && (!$config['skip_config_tests'])) {
+ require_once(INCLUDE_DIR. '/admin_checks.php');
+}
+
// Create our pages array from existing files
if (is_dir(INCLUDE_DIR . '/pages/')) {
foreach (glob(INCLUDE_DIR . '/pages/*.inc.php') as $filepath) {
diff --git a/public/templates/mpos/login/default.tpl b/public/templates/mpos/login/default.tpl
index b9c8ef56..b407e2a1 100644
--- a/public/templates/mpos/login/default.tpl
+++ b/public/templates/mpos/login/default.tpl
@@ -1,6 +1,5 @@