sanitize(); } // deal with array's or array emulating objects elseif (is_array($var) or ($var instanceOf \Traversable and $var instanceOf \ArrayAccess)) { // recurse on array values foreach($var as $key => $value) { $var[$key] = static::clean($value, $filters, $type); } } // deal with all other variable types else { is_null($filters) and $filters = \Config::get($type, array()); $filters = is_array($filters) ? $filters : array($filters); foreach ($filters as $filter) { // is this filter a callable local function? if (is_string($filter) and is_callable('static::'.$filter)) { $var = static::$filter($var); } // is this filter a callable function? elseif (is_callable($filter)) { $var = call_user_func($filter, $var); } // assume it's a regex of characters to filter else { $var = preg_replace('#['.$filter.']#ui', '', $var); } } } return $var; } public static function xss_clean($value, array $options = array()) { if ( ! is_array($value)) { if ( ! function_exists('htmLawed')) { import('htmlawed/htmlawed', 'vendor'); } return htmLawed($value, array_merge(array('safe' => 1, 'balanced' => 0), $options)); } foreach ($value as $k => $v) { $value[$k] = static::xss_clean($v); } return $value; } public static function strip_tags($value) { if ( ! is_array($value)) { $value = filter_var($value, FILTER_SANITIZE_STRING); } else { foreach ($value as $k => $v) { $value[$k] = static::strip_tags($v); } } return $value; } public static function htmlentities($value, $flags = null, $encoding = null, $double_encode = null) { static $already_cleaned = array(); is_null($flags) and $flags = \Config::get('security.htmlentities_flags', ENT_QUOTES); is_null($encoding) and $encoding = \Fuel::$encoding; is_null($double_encode) and $double_encode = \Config::get('security.htmlentities_double_encode', false); // Nothing to escape for non-string scalars, or for already processed values if (is_bool($value) or is_int($value) or is_float($value) or in_array($value, $already_cleaned, true)) { return $value; } if (is_string($value)) { $value = htmlentities($value, $flags, $encoding, $double_encode); } elseif (is_object($value) and $value instanceOf \Sanitization) { $value->sanitize(); return $value; } elseif (is_array($value) or ($value instanceof \Iterator and $value instanceof \ArrayAccess)) { // Add to $already_cleaned variable when object is_object($value) and $already_cleaned[] = $value; foreach ($value as $k => $v) { $value[$k] = static::htmlentities($v, $flags, $encoding, $double_encode); } } elseif ($value instanceof \Iterator or get_class($value) == 'stdClass') { // Add to $already_cleaned variable $already_cleaned[] = $value; foreach ($value as $k => $v) { $value->{$k} = static::htmlentities($v, $flags, $encoding, $double_encode); } } elseif (is_object($value)) { // Check if the object is whitelisted and return when that's the case foreach (\Config::get('security.whitelisted_classes', array()) as $class) { if (is_a($value, $class)) { // Add to $already_cleaned variable $already_cleaned[] = $value; return $value; } } // Throw exception when it wasn't whitelisted and can't be converted to String if ( ! method_exists($value, '__toString')) { throw new \RuntimeException('Object class "'.get_class($value).'" could not be converted to string or '. 'sanitized as ArrayAccess. Whitelist it in security.whitelisted_classes in app/config/config.php '. 'to allow it to be passed unchecked.'); } $value = static::htmlentities((string) $value, $flags, $encoding, $double_encode); } return $value; } /** * Check CSRF Token * * @param string $value CSRF token to be checked, checks post when empty * @return bool */ public static function check_token($value = null) { $value = $value ?: \Input::param(static::$csrf_token_key, \Input::json(static::$csrf_token_key, 'fail')); // always reset token once it's been checked and still the same if (static::fetch_token() == static::$csrf_old_token and ! empty($value)) { static::set_token(true); } return $value === static::$csrf_old_token; } /** * Fetch CSRF Token for the next request * * @return string */ public static function fetch_token() { if (static::$csrf_token !== false) { return static::$csrf_token; } static::set_token(); return static::$csrf_token; } /** * Generate new token. Based on an example from OWASP * * @return string */ public static function generate_token() { // generate a random token base if (function_exists('random_bytes')) { $token_base = \Config::get('security.token_salt', '') .random_bytes(64); } elseif (function_exists('openssl_random_pseudo_bytes')) { $token_base = \Config::get('security.token_salt', '') . openssl_random_pseudo_bytes(64); } else { $token_base = time() . uniqid() . \Config::get('security.token_salt', '') . mt_rand(0, mt_getrandmax()); } // return the hashed token if (function_exists('hash_algos')) { foreach (array('sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5') as $hash) { if (in_array($hash, hash_algos())) { return hash($hash, $token_base); } } } // if all else fails return md5($token_base); } protected static function set_token($reset = false) { // re-use old token when found (= not expired) and expiration is used (otherwise always reset) if ( ! $reset and static::$csrf_old_token and \Config::get('security.csrf_expiration', 0) > 0) { static::$csrf_token = static::$csrf_old_token; } // set new token for next session when necessary else { static::$csrf_token = static::generate_token(); $expiration = \Config::get('security.csrf_expiration', 0); \Cookie::set(static::$csrf_token_key, static::$csrf_token, $expiration); } } /** * JS fetch token * * Produces JavaScript fuel_csrf_token() function that will return the current * CSRF token when called. Use to fill right field on form submit for AJAX operations. * * @return string */ public static function js_fetch_token() { $output = ''.PHP_EOL; return $output; } /** * JS set token * * Produces JavaScript fuel_set_csrf_token() function that will update the current * CSRF token in the form when called, based on the value of the csrf cookie * * @return string */ public static function js_set_token() { $output = ''.PHP_EOL; return $output; } }