'', 'value' => '', //'default' => '', 'name' => '', //'id' => '', 'safemode' => '', 'path' => '', 'thumbnail_1' => '', 'thumbnail_2' => '', ]; private static $input_error_text = []; public function __construct() { } /** * Generates and return class instance * Eliminates global usage in functions * Instead of using - `global $defender`, try `\defender->getInstance()` * * @return null|static */ public static function getInstance() { if (self::$defender_instance === NULL) { self::$defender_instance = new static(); } return self::$defender_instance; } public static function set_remoteFile($file) { self::$remote_file = $file; } /** * Request whether safe to proceed at all times * * @return bool */ public static function safe() { if (!defined("FUSION_NULL")) { return TRUE; } return FALSE; } /** * Serialize an array securely * * @param array $array * * @return string */ public static function serialize(array $array = []) { $return_default = ""; if (is_array($array)) { return base64_encode(serialize($array)); } return $return_default; } public static function encode($value) { return base64_encode(json_encode($value)); } public static function decode($value) { return json_decode(base64_decode($value), TRUE); } /** * Read serialized array * * @param $string - serialized string * * @return array|mixed */ public static function unserialize($string) { $return_default = []; if (!empty($string)) { $array = unserialize(base64_decode($string)); if (!empty($array)) { return $array; } } return $return_default; } public static function add_field_session(array $array) { $_SESSION['form_fields'][self::pageHash()][$array['input_name']] = $array; } /** * @param array $array */ public function addHoneypot(array $array) { $_SESSION['honeypots'][self::pageHash()][$array['honeypot']] = $array; } /** * @param string $honeypot * * @return string */ public function getHoneypot($honeypot = '') { if ($honeypot && isset($_SESSION['honeypots'][self::pageHash()][$honeypot])) { return $_SESSION['honeypots'][self::pageHash()][$honeypot]; } else { if ($honeypot) { return "This form contains no honeypots"; } else { return $_SESSION['honeypots'][self::pageHash()]; } } } private static $page_hash = ''; /** * Hash a token to prevent unauthorized access * * @return string */ public static function pageHash() { if (!defined('SECRET_KEY')) { $chars = ["abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ", "123456789"]; $count = [(strlen($chars[0]) - 1), (strlen($chars[1]) - 1)]; $key = ""; for ($i = 0; $i < 32; $i++) { $type = mt_rand(0, 1); $key .= substr($chars[$type], mt_rand(0, $count[$type]), 1); } define('SECRET_KEY', $key); } if (empty(self::$page_hash)) { self::$page_hash = md5(SECRET_KEY); } return (string)self::$page_hash; } // Checks whether an input was marked as invalid /** * Return the current document field session or sessions * Use for debug purposes * * @param string $input_name * * @return string */ public static function get_current_field_session($input_name = '') { if ($input_name && isset($_SESSION['form_fields'][self::pageHash()][$input_name])) { return $_SESSION['form_fields'][self::pageHash()][$input_name]; } else { if ($input_name) { return "The session for this field is not found"; } else { //return $_SESSION['form_fields']; return $_SESSION['form_fields'][self::pageHash()]; } } } public static function unset_field_session() { unset($_SESSION['form_fields']); } static function sanitize_array($array) { foreach ($array as $name => $value) { $array[stripinput($name)] = trim(censorwords(stripinput($value))); } return (array)$array; } /** * ID for Session * No $userName because it can be changed and tampered via Edit Profile. * Using IP address extends for guest * * @return mixed */ public static function set_sessionUserID() { $userdata = fusion_get_userdata(); return !empty($userdata['user_id']) && !isset($_POST['login']) ? (int)fusion_get_userdata('user_id') : str_replace('.', '-', USER_IP); } /** * @param bool|FALSE $value */ public function debug($value = FALSE) { self::$debug = $value; } /** * @return array */ public static function getInputErrors() { return self::$input_errors; } /** * Set and override default field error text * * @param $input_name * @param $text */ public static function setErrorText($input_name, $text) { self::$input_error_text[$input_name] = $text; } /** * Fetches the latest error text of this input * Important! Ensure your applications do not refresh screen for this error to show. * Usage \defender::safe(); for conditional redirect. * * @param $input_name * * @return null */ public static function getErrorText($input_name) { if (self::inputHasError($input_name)) { return isset(self::$input_error_text[$input_name]) ? self::$input_error_text[$input_name] : NULL; } return NULL; } public static function inputHasError($input_name) { if (isset(self::$input_errors[$input_name])) { return TRUE; } return FALSE; } public static function get_inputError() { return self::$input_errors; } /** * @param $key * * @return string */ public function filterPostArray($key) { $flag = ''; $input_key = $key; if (is_array($key)) { // always use key 0 for filter var $input_key = $key[0]; $flag = FILTER_REQUIRE_ARRAY; } $filtered = post($input_key, FILTER_DEFAULT, $flag); if (is_array($key)) { $input_ref = $key; unset($input_ref[0]); // Get the value of the filtered post value using the $key array as map return array_reduce($input_ref, function ($value, $key) { return (!empty($value[$key]) ? $value[$key] : ''); }, $filtered); } return (string)$filtered; } /** * Sanitize * * @param $value * @param string $default * @param bool|FALSE $input_name * @param bool|FALSE $is_multiLang * * @return string|mixed * @throws Exception */ public function form_sanitizer($value, $default = "", $input_name = FALSE, $is_multiLang = FALSE) { $val = []; if ($input_name) { if ($is_multiLang) { foreach (fusion_get_enabled_languages() as $lang => $language) { $iname = $input_name."[".$lang."]"; if (isset($_SESSION['form_fields'][self::pageHash()][$iname])) { $this->field_config = $_SESSION['form_fields'][self::pageHash()][$iname]; $this->field_name = $iname; $this->field_value = $value[$lang]; $this->field_default = $default; $val[$lang] = $this->validate(); } } if ($this->field_config['required'] && (!$value[LANGUAGE])) { self::stop(); $iname = $input_name."[".LANGUAGE."]"; self::setInputError($iname); return $default; } else { $val_ = []; foreach ($val as $lang => $value) { $val_[$lang] = $val[$lang]; } return serialize($val_); } } else { // Make sure that the input was actually defined in code.. AND there must be a value to worth the processing power expense! if (isset($_SESSION['form_fields'][self::pageHash()][$input_name])) { $this->field_config = $_SESSION['form_fields'][self::pageHash()][$input_name]; $this->field_name = $input_name; $this->field_value = $value; $this->field_default = $default; /* * These two checks won't be neccesary after we add the options in all inputs * NOTE: Please don't pass 'stripinput' as callback, before we reach a callback * everything is checked and sanitized already. The callback should only check * if certain conditions are met then return TRUE|FALSE and not do any alterations * the the value itself */ $callback = isset($this->field_config['callback_check']) ? $this->field_config['callback_check'] : FALSE; $regex = isset($this->field_config['regex']) ? $this->field_config['regex'] : FALSE; $secured = $this->validate(); // If truly FALSE the check failed if ($secured === FALSE || ($this->field_config['required'] == 1 && ($secured === FALSE || $secured == '')) || ($secured != '' && $regex && !preg_match('@^'.$regex.'$@i', $secured)) || // regex will fail for an imploded array, maybe move this check (is_callable($callback) && !$callback($secured)) ) { self::stop(); self::setInputError($input_name); // Add regex error message. if ($secured != '' && $regex && !preg_match('@^'.$regex.'$@i', $secured)) { addNotice("danger", sprintf(fusion_get_locale('regex_error'), $this->field_config['title'])); unset($locale); } // Add a notice if (self::$debug) { addNotice('warning', ''.$input_name.':'.($this->field_config['safemode'] ? ' is in SAFEMODE and the' : '').' check failed'); } // Return user's input for correction return $this->field_value; } else { if (self::$debug) { addNotice('info', $input_name.' = '.(is_array($secured) ? 'array' : $secured)); } return $secured; } } else { return $default; } } } else { if ($value) { if (!is_array($value)) { if (intval($value)) { return stripinput($value); // numbers } else { return censorwords(stripinput(trim(preg_replace("/ +/i", " ", $value)))); } } else { $secured = []; foreach ($value as $arr => $unsecured) { if (intval($unsecured)) { $secured[] = stripinput($unsecured); // numbers } else { $secured[] = censorwords(stripinput(trim(preg_replace("/ +/i", " ", $unsecured)))); } } return implode($this->field_config['delimiter'], $secured); } } else { return $default; } } throw new \Exception('The form sanitizer could not handle the request! (input: '.$input_name.')'); } /** * Sanitize with input name * * @param $key * @param string $default * @param bool $input_name * @param bool $is_multiLang * * @return string * @throws Exception */ public function sanitizer($key, $default = '', $input_name = FALSE, $is_multiLang = FALSE) { $value = $this->filterPostArray($key); return $this->form_sanitizer($value, $default, $input_name, $is_multiLang); } public function fileSanitizer($key, $default = '', $input_name = FALSE) { $upload = $this->form_sanitizer($_FILES[$key], $default, $input_name); if (isset($upload['error']) && $upload['error'] == 0) { return $upload; } return []; } /** @noinspection PhpInconsistentReturnPointsInspection */ public function validate() { \defender\Validation::inputName($this->field_name); \defender\Validation::inputDefault($this->field_default); \defender\Validation::inputValue($this->field_value); \defender\Validation::inputConfig($this->field_config); $locale = fusion_get_locale(LOCALE.LOCALESET.'defender.php'); // Are there situations were inputs could have leading // or trailing spaces? If not then uncomment line below //$this->field_value = trim($this->field_value); // Don't bother processing and validating empty inputs //if ($this->field_value == '') return $this->field_value; /** * Keep this include in the constructor * This solution was needed to load the defender.inc.php before * defining LOCALESET */ // declare the validation rules and assign them // type of fields vs type of validator // execute sanitisation rules at point blank precision using switch try { if (!empty($this->field_config['type'])) { if (empty($this->field_value) && ($this->field_config['type'] !== "number")) { return $this->field_default; } return \defender\Validation::getValidated(); } else { self::stop(); $locale['type_unknown'] = '%s: has an unknown type set'; // to be moved addNotice('danger', sprintf($locale['type_unknown'], self::$inputName)); } } catch (Exception $e) { self::stop(); addNotice('danger', $e->getMessage()); } return NULL; } /** * Send an Unsafe Signal acorss all PHP-Fusion Components * This will automatically halt on all important execution without exiting. * * @param $notice - Show custom notice message on validation errors */ public static function stop($notice = '') { if (!defined('FUSION_NULL')) { define('FUSION_NULL', TRUE); if ($notice) { if (function_exists('alert')) { echo alert($notice); } else { addNotice("danger", $notice); } } //addNotice('danger', ''.fusion_get_locale('error_request', LOCALE.LOCALESET.'defender.php').''); if (self::$debug) { debug_print_backtrace(); define("STOP_REDIRECT", TRUE); } } } /* * Highlight the form field as error */ public static function setInputError($input_name) { self::$input_errors[$input_name] = TRUE; } /** * Generate a key * * @param $private_key * * @return string */ public static function get_encrypt_key($private_key) { return $encryption_key = openssl_random_pseudo_bytes(32, $private_key); // 256 bits } private static function pkcs7_pad($data, $size) { $length = $size - strlen($data) % $size; return $data.str_repeat(chr($length), $length); } private static function pkcs7_unpad($data) { return substr($data, 0, -ord($data[strlen($data) - 1])); } /** * Encrypts a string securely with a private key * * @param string $string The text to encrypt * @param string $private_key For better security use \defender::get_encrypt_key to generate your private key * * Does not support array encrypt. * * @return string */ public static function encrypt_string($string, $private_key = 'php-fusion') { $ivlen = openssl_cipher_iv_length($cipher = "AES-128-CBC"); $iv = openssl_random_pseudo_bytes(16, $ivlen); // 128 bits $string = self::pkcs7_pad($string, 16); $ciphertext_raw = openssl_encrypt($string, $cipher, $private_key, $options = OPENSSL_RAW_DATA, $iv); $hmac = hash_hmac('sha256', $ciphertext_raw, $private_key, $as_binary = TRUE); return base64_encode($iv.$hmac.$ciphertext_raw); } /** * Decrypts a string securely with a private key * * @param $string - The string to decrypt * @param $private_key - For better security use \defender::get_encrypt_key to generate your private key * * @return null|string */ public static function decrypt_string($string, $private_key = 'php-fusion') { $c = base64_decode($string); $ivlen = openssl_cipher_iv_length($cipher = "AES-128-CBC"); $iv = substr($c, 0, $ivlen); $hmac = substr($c, $ivlen, $sha2len = 32); $ciphertext_raw = substr($c, $ivlen + $sha2len); $string = openssl_decrypt($ciphertext_raw, $cipher, $private_key, $options = OPENSSL_RAW_DATA, $iv); $string = self::pkcs7_unpad($string); $calcmac = hash_hmac('sha256', $ciphertext_raw, $private_key, $as_binary = TRUE); if (!function_exists('hash_equals')) { function hash_equals($str1, $str2) { if (strlen($str1) != strlen($str2)) { return FALSE; } else { $res = $str1 ^ $str2; $ret = 0; for ($i = strlen($res) - 1; $i >= 0; $i--) { $ret |= ord($res[$i]); } return !$ret; } } } if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison { return $string; } return NULL; } } /** * Verify and Sanitize Inputs * * @param $value * @param string $default * @param bool|FALSE $input_name * @param bool|FALSE $is_multiLang * * @return mixed * @throws Exception */ function form_sanitizer($value, $default = "", $input_name = FALSE, $is_multiLang = FALSE) { return defender::getInstance()->form_sanitizer($value, $default, $input_name, $is_multiLang); } /** * Verify and Sanitize Inputs with input_name * A more secured method * * @param $value * @param string $default * @param bool $input_name * @param bool $is_multiLang * * @return string * @throws Exception */ function sanitizer($value, $default = '', $input_name = FALSE, $is_multiLang = FALSE) { return defender::getInstance()->sanitizer($value, $default, $input_name, $is_multiLang); } function file_sanitizer($value, $default = '', $input_name = FALSE) { return defender::getInstance()->fileSanitizer($value, $default, $input_name); } /** * @param null $key * @param int $type * @param string $flags * * @return mixed */ function get($key = NULL, $type = FILTER_DEFAULT, $flags = '') { if ($flags == FILTER_VALIDATE_INT) { if (isset($_GET[$key]) && isnum($_GET[$key]) && $_GET[$key] > PHP_INT_MAX) { return intval($_GET[$key]); } return intval(0); } return stripinput(filter_input(INPUT_GET, $key, $type, $flags)); } /** * @param $key * @param int $type * @param $flags * * @return mixed */ function post($key, $type = FILTER_DEFAULT, $flags = '') { if (is_array($key)) { $flag = ''; $input_key = $key; if (is_array($key)) { // always use key 0 for filter var $input_key = $key[0]; $flag = FILTER_REQUIRE_ARRAY; } $filtered = post($input_key, FILTER_DEFAULT, $flag); if (is_array($key)) { $input_ref = $key; unset($input_ref[0]); // Get the value of the filtered post value using the $key array as map return array_reduce($input_ref, function ($value, $key) { return (!empty($value[$key]) ? $value[$key] : ''); }, $filtered); } return (string)$filtered; } if ($flags == FILTER_VALIDATE_INT) { if (isset($_POST[$key]) && isnum($_POST[$key]) && $_POST[$key] > PHP_INT_MAX) { return intval($_GET[$key]); } } return filter_input(INPUT_POST, $key, $type, $flags); } /** * @param $key * * @return array */ function post_array($key) { return (array)\defender::getInstance()->filterPostArray($key); } /** * @param $key * @param int $type * * @return mixed */ function server($key, $type = FILTER_DEFAULT) { return filter_input(INPUT_SERVER, $key, $type); } /** * @param $key * * @return bool */ function file_uploaded($key) { if (!empty($_FILES)) { if (is_array($key)) { $files =& $_FILES; foreach ($key as $pkey) { $files =& $files[$pkey]; } return is_uploaded_file($files['tmp_name']); } return is_uploaded_file($_FILES[$key]['tmp_name']); } return FALSE; } /** * @param $key * @param int $type * * @return mixed */ function environment($key, $type = FILTER_DEFAULT) { return filter_input(INPUT_ENV, $key, $type); } /** * @param $key * @param int $type * * @return mixed */ function cookie($key, $type = FILTER_DEFAULT) { return stripinput(filter_input(INPUT_COOKIE, $key, $type)); } function fusion_safe() { return defender::safe(); } require_once __DIR__.'/defender/validation.php'; require_once __DIR__.'/defender/token.php'; require_once __DIR__.'/defender/mimecheck.php';