merged session manager/memcache limiter

cleanup for PR
This commit is contained in:
xisi 2014-01-28 06:12:00 -05:00
parent c373fc7192
commit 6398e5dfec
15 changed files with 566 additions and 251 deletions

2
.gitignore vendored
View File

@ -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/*

View File

@ -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/");

View File

@ -0,0 +1,113 @@
<?php
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
if (@$_SESSION['USERDATA']['is_admin'] && $user->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 <u>SET and INVALID</u>";
}
}
} 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, <u>memcache is required</u> 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 <u>disabled</u> - 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 <u>SHOULD NOT leave your SALT or SALTY default</u>";
} 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');
}
}
}
?>

View File

@ -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');
}
?>

View File

@ -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;
}
}
?>

View File

@ -1,108 +1,140 @@
<?php
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
class SessionManager {
private $bind_address = '';
private $started = false;
private $host_verified = false;
private $config_dura = 0;
private $config_path = '';
private $config_domain = '';
private $config_secure = false;
private $config_httponly = false;
private $server_http_host = null;
private $current_session_id = '';
private $current_session_ip = '';
public $memcache_handle = null;
public function set_cookie_params($duration, $path, $domain, $secure, $httponly) {
session_set_cookie_params((time()+$duration), $path, $domain, $secure, $httponly);
class strict_session {
private $memcache = null;
private $validate_client = false;
private $validate_client_ip = false;
private $validate_client_ua = false;
private $validate_client_sid = false;
private $validate_client_num = 0;
private $valid_server = '';
private $memcache_key = '';
public function valid_session_id($id) {
return preg_match('#^[a-zA-Z0-9]{26}$#', $id);
}
public function verify_server() {
if ($this->bind_address !== $this->server_http_host) {
return false;
} else {
return true;
public function session_delete_key($key) {
$read = $this->memcache->delete($key);
}
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);
}
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;
}
} else {
$update_client = $this->memcache->set($client['key'], $client);
if ($force && $login) {
$update_client = $this->memcache->set($update['key'], $update);
}
}
}
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('<meta http-equiv="refresh" content="0; url=' . htmlspecialchars($location) . '"/>');
}
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;
}
}
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()));
}
$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']);
}
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_set_cookie_params((time()+$config['cookie']['duration']), $config['cookie']['path'], $config['cookie']['domain'], false, true);
$session_start = @session_start();
if (!$session_start) {
session_destroy();
session_regenerate_id(true);
$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();
$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;
}
}
}
if ($read !== null) {
// client exists, verify
$this->create_or_update_client($client, true, false);
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);
} 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));
}
}
}

View File

@ -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_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('<meta http-equiv="refresh" content="0; url=' . $location . '"/>');
}

View File

@ -1,25 +1,6 @@
<?php
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
/**
* Forces extra security options when enabled
*
* You must have Memcache enabled and configured & Memcache anti-dos configured to use this.
*
* Check -> 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
*

View File

@ -0,0 +1,136 @@
<?php
/**
* Strict is a set of extra security options can use that when enabled can help protect against
* a few different types of attacks.
*
* You must have Memcache enabled and configured & Memcache anti-dos configured to use this!
*
* Check -> 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;
?>

View File

@ -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']);

View File

@ -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('<meta http-equiv="refresh" content="0; url=' . htmlspecialchars($location) . '"/>');
} 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');
?>

View File

@ -1,7 +1,18 @@
<?php
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
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");
?>

View File

@ -1,7 +1,7 @@
<?php
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
define('MPOS_VERSION', '0.0.2');
define('MPOS_VERSION', '0.0.3');
define('DB_VERSION', '0.0.4');
define('CONFIG_VERSION', '0.0.7');

View File

@ -16,11 +16,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Set a decently long SECURITY key with special chars etc
define('SECURITY', '*)WT#&YHfd');
// Whether or not to check SECHASH for validity, still checks if SECURITY defined as before if disabled
define('SECHASH_CHECK', true);
define('SECHASH_CHECK', false);
// Nothing below here to configure, move along...
@ -30,7 +29,7 @@ if (SECHASH_CHECK) {
define('SECHASH', fip());
function cfip() { return (fip()==SECHASH||fip(1)==SECHASH||fip(2)==SECHASH) ? 1 : 0; }
} else {
function cfip() { return (defined('SECURITY')) ? 1 : 0; }
function cfip() { return (@defined('SECURITY')) ? 1 : 0; }
}
// Used for performance calculations
@ -42,6 +41,11 @@ define("BASEPATH", dirname(__FILE__) . "/");
// Include our configuration (holding defines for the requires)
if (!include_once(BASEPATH . 'include/config/global.inc.php')) die('Unable to load site configuration');
if (!include_once(BASEPATH . 'include/config/security.inc.php')) die('Unable to load security configuration');
// switch to https if config option is enabled
$hts = ($config['strict__https_only'] && (!empty($_SERVER['QUERY_STRING']))) ? "https://".$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME']."?".$_SERVER['QUERY_STRING'] : "https://".$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'];
($config['strict__https_only'] && @!$_SERVER['HTTPS']) ? exit(header($hts)):0;
// Our default template to load, pages can overwrite this later
$master_template = 'master.tpl';
@ -52,26 +56,26 @@ require_once(INCLUDE_DIR . '/autoloader.inc.php');
if ($config['memcache']['enabled'] && ($config['mc_antidos']['enabled'] || $config['strict'])) {
if (PHP_OS == 'WINNT') {
require_once('memcached.class.php');
require_once(CLASS_DIR . 'memcached.class.php');
}
// strict mode and memcache antidos need a memcache handle
$memcache = new Memcached();
$memcache->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) {

View File

@ -1,6 +1,5 @@
<article class="module width_half">
<form action="{$smarty.server.SCRIPT_NAME}?page=login" method="post" id="loginForm">
<input type="hidden" name="to" value="{($smarty.request.to|default:"{$smarty.server.SCRIPT_NAME}?page=dashboard")|escape}" />
<input type="hidden" name="ctoken" value="{$CTOKEN|escape|default:""}" />
<header><h3>Login with existing account</h3></header>
<div class="module_content">