diff --git a/application/core/EA_Security.php b/application/core/EA_Security.php index ed7edae5..44046de0 100644 --- a/application/core/EA_Security.php +++ b/application/core/EA_Security.php @@ -40,5 +40,59 @@ * @property EA_URI $uri */ class EA_Security extends CI_Security { - // + /** + * CSRF Verify + * + * @return CI_Security + */ + public function csrf_verify() + { + // If it's not a POST request we will set the CSRF cookie + if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') + { + return $this->csrf_set_cookie(); + } + + // Check if URI has been whitelisted from CSRF checks + if ($exclude_uris = config_item('csrf_exclude_uris')) + { + $uri = load_class('URI', 'core'); + foreach ($exclude_uris as $excluded) + { + if (preg_match('#^' . $excluded . '$#i' . (UTF8_ENABLED ? 'u' : ''), $uri->uri_string())) + { + return $this; + } + } + } + + // Check CSRF token validity, but don't error on mismatch just yet - we'll want to regenerate + $csrf_token = $_POST[$this->_csrf_token_name] ?? $_SERVER['HTTP_X_CSRF'] ?? NULL; + + $valid = isset($csrf_token, $_COOKIE[$this->_csrf_cookie_name]) + && is_string($csrf_token) && is_string($_COOKIE[$this->_csrf_cookie_name]) + && hash_equals($csrf_token, $_COOKIE[$this->_csrf_cookie_name]); + + // We kill this since we're done and we don't want to pollute the _POST array + unset($_POST[$this->_csrf_token_name]); + + // Regenerate on every submission? + if (config_item('csrf_regenerate')) + { + // Nothing should last forever + unset($_COOKIE[$this->_csrf_cookie_name]); + $this->_csrf_hash = NULL; + } + + $this->_csrf_set_hash(); + $this->csrf_set_cookie(); + + if ($valid !== TRUE) + { + $this->csrf_show_error(); + } + + log_message('info', 'CSRF token verified'); + return $this; + } }