From 7076343338ae3439f3c86f01144818abe8c31978 Mon Sep 17 00:00:00 2001
From: George Halkiadakis
Date: Sun, 12 Mar 2023 07:16:23 +0200
Subject: add container helpers; constuct public directory-tree
---
classes/Benchmark.php | 150 ----
classes/Cart.php | 71 --
classes/Database.php | 331 ---------
classes/Registry.php | 129 ----
classes/Repository.php | 43 --
classes/Request.php | 94 ---
classes/Route.php | 97 ---
classes/Security.php | 923 -------------------------
classes/User.php | 111 ---
classes/authenticator/PasswordTrait.php | 40 --
classes/authenticator/User.php | 102 ---
classes/authenticator/UserInterface.php | 15 -
classes/authenticator/UserManager.php | 69 --
classes/authenticator/UserManagerInterface.php | 21 -
classes/authenticator/UserToken.php | 33 -
classes/authenticator/UserTokenInterface.php | 13 -
classes/authenticator/info.md | 114 ---
classes/cache/Cache_interface.php | 34 -
classes/cache/FileCache.php | 93 ---
classes/cache/MemcachedCache.php | 37 -
classes/cache/RedisCache.php | 69 --
classes/session/DatabaseSession.php | 138 ----
classes/session/DefaultSession.php | 35 -
classes/session/FilesSession.php | 142 ----
classes/session/SessionHandlerInterface.php | 169 -----
25 files changed, 3073 deletions(-)
delete mode 100644 classes/Benchmark.php
delete mode 100644 classes/Cart.php
delete mode 100644 classes/Database.php
delete mode 100644 classes/Registry.php
delete mode 100644 classes/Repository.php
delete mode 100644 classes/Request.php
delete mode 100644 classes/Route.php
delete mode 100644 classes/Security.php
delete mode 100644 classes/User.php
delete mode 100644 classes/authenticator/PasswordTrait.php
delete mode 100644 classes/authenticator/User.php
delete mode 100644 classes/authenticator/UserInterface.php
delete mode 100644 classes/authenticator/UserManager.php
delete mode 100644 classes/authenticator/UserManagerInterface.php
delete mode 100644 classes/authenticator/UserToken.php
delete mode 100644 classes/authenticator/UserTokenInterface.php
delete mode 100644 classes/authenticator/info.md
delete mode 100644 classes/cache/Cache_interface.php
delete mode 100644 classes/cache/FileCache.php
delete mode 100644 classes/cache/MemcachedCache.php
delete mode 100644 classes/cache/RedisCache.php
delete mode 100644 classes/session/DatabaseSession.php
delete mode 100644 classes/session/DefaultSession.php
delete mode 100644 classes/session/FilesSession.php
delete mode 100644 classes/session/SessionHandlerInterface.php
(limited to 'classes')
diff --git a/classes/Benchmark.php b/classes/Benchmark.php
deleted file mode 100644
index a7e93ea..0000000
--- a/classes/Benchmark.php
+++ /dev/null
@@ -1,150 +0,0 @@
-';
- $close = '
';
- $divider = ":";
- $prefix = '';
- $suffix = '
';
- break;
- case 'code':
- $open = '';
- $close = '';
- $divider = ": ";
- $prefix = '';
- $suffix = '
';
- break;
- case 'comment':
- default:
- $open = '';
- $divider = ":";
- $prefix = '';
- $suffix = '';
- break;
- }
-
- echo $prefix;
-
- // TIME REPORT
- // ---------------------------------------------------------------------
- echo "\n\n{$open} Timings {$close}";
-
- // valid from php 7.3
- // $start = self::$timeSpots[ array_key_first(self::$timespots) ];
- // $end = self::$timeSpots[ array_key_last(self::$timespots) ];
- $start = self::$timeSpots['start'];
- $end = self::$timeSpots['end'];
- $all_dt = number_format($end - $start , 4)*1000 ." ms";
-
-
-
- // if more than 2 timespots
- // echo dt between each spot
- if (count(self::$timeSpots) > 2) {
-
- // calculate dt between spots
- $time_results = array();
- $prev_key = '';
- $prev_time = 0;
- foreach(self::$timeSpots as $key => $t) {
- if (!$prev_time) {
- $prev_time = $t;
- $prev_key = $key;
- }
- else {
- $dt = number_format($t - $prev_time , 4)*1000 ." ms";
- $time_results[] = array(
- 'part' => "{$prev_key}[..{$key}]", ///$key,
- 'dt' => $dt
- );
- $prev_key = $key; //// "{$prev_key}[..{$key}]";
- $prev_time = $t;
- }
- }
-
- // echo timings
- foreach ($time_results as $key => $val) {
- echo "\n\t{$open} {$val['part']} {$divider} {$val['dt']} {$close}";
- }
- }
-
- // echo total time
- echo "\n\t{$open} total {$divider} {$all_dt} {$close}";
-
-
- // MEMORY REPORT
- // ---------------------------------------------------------------------
- echo "\n\n{$open} Memory Usage {$close}";
-
- if (count(self::$memoryUse) > 2) {
-
- foreach (self::$memoryUse as $key => $val) {
- echo "\n\t{$open} {$key} {$divider} {$val}MB {$close}";
- }
-
- } else {
- echo "\n\t{$open} memory usage {$divider} ". round(memory_get_usage()/(1024*1024),2) ."MB {$close}";
- }
-
- echo $suffix;
-
- }
-
- }
-}
diff --git a/classes/Cart.php b/classes/Cart.php
deleted file mode 100644
index 4e6f585..0000000
--- a/classes/Cart.php
+++ /dev/null
@@ -1,71 +0,0 @@
-uid], 10)
- //
- // oranize in results = [
- // {
- // oid,
- // prods: [
- // {
- // pid,
- // prodlabel,
- // count
- // },
- // ...
- // ]
- // },
- // ...,
- // ]
- //
- // return $results
-
- }
-}
-
-
-
-/* ---
-orders:
- : id
- : uid (user id)
-
-order_products
- : id
- : oid (order id)
- : pid (product id)
- -- */
\ No newline at end of file
diff --git a/classes/Database.php b/classes/Database.php
deleted file mode 100644
index 35ff65f..0000000
--- a/classes/Database.php
+++ /dev/null
@@ -1,331 +0,0 @@
-throw_errors = PRODUCTION ? false : true;
-
- if (null == $this->connection) {
- try {
- $this->connection = new PDO(
- "mysql:" . PDO_HOST . ";" . "dbname=" . DB_NAME,
- DB_USER,
- DB_PASS,
- array(
- PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'",
- PDO::MYSQL_ATTR_LOCAL_INFILE => true
- )
- );
-
- // handle error reporting
- if ($this->throw_errors) {
- $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
- }
- $this->setTimezone();
-
- } catch (PDOException $exc) { handle_exception($exc); }
- }
- # return $this->connection;
- }
-
-
- /** disconnect
- * CHECK: not sure if needed
- */
- public function disconnect()
- {
- $this->connection = null;
- }
-
-
- /** set Timezone
- * (Self-explanatory)
- */
- public function setTimezone($timezone = DB_TIMEZONE) {
- // $this->connection->prepare($timezone)->execute();
- }
-
-
- /** lastInsertID
- * returns the ID of last inserted record
- */
- public function lastInsertID()
- {
- return $this->connection->insert_id;
- }
-
-
- /** query
- * ---
- * set and execute a query safely;
- * save results as associative array; DO NOT RETURN RESULTS
- * @param $sql (string): SQL query
- * @param $args (array): array of values to bind into SQL
- * @param $pypass (boolean): flag to bypass security check
- * @return $this (database handler)
- */
- public function query($sql, $args=[])
- {
- try {
-
- $stmt = $this->connection->prepare($sql);
-
- if ($args == []) {
- $result = $stmt->execute();
-
- } else {
- $result = $stmt->execute($args);
-
- }
-
- $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
-
- $this->result = $result;
-
- return $this;
-
- } catch (PDOException $exc) { handle_exception($exc); }
-
- }
-
-
- /** getAll
- * ---
- * return all resulted records
- * use it after db->query();
- */
- public function getAll()
- {
- return ($this->result === null) ? false : $this->result;
- }
-
-
- /** getFirst
- * ---
- * get first row of the resulted query;
- * used when one row is expected
- * ex. $db->query('SELECT * FROM users WHERE id = :id',['id'=>1])->getFirst();
- */
- public function getFirst()
- {
- if (($this->result === null) || ($this->result == [])) {
- return false;
-
- } else { return $this->result[0]; }
- }
-
-
- /** getOnly
- * ---
- * return the first column value of the first row
- * used when only one value is needed
- * ex. $db->query('SELECT Count(id) FROM table',[])->getOnly();
- */
- public function getOnly()
- {
- if (($this->result === null) || ($this->result == [])) {
- return false;
-
- } else { return array_values($this->result[0])[0]; }
- }
-
-
- /** runQuery
- * --- (shortcut method)
- * execute a query safely;
- * return results as associative array
- * @param $sql (string): SQL query
- * @param $args (array): array of values to bind into SQL
- * @param $pypass (boolean): flag to bypass security check
- */
- public function runQuery($sql, $args=[])
- {
- return $this->query($sql, $args)->getAll();
- }
-
-
- /** runLimitQuery( sql, args, limit=100, offset = null )
- * set LIMIT / OFFSET clauses in a secure way
- * @param $sql (string): SQL query
- * @param $args (array): array of values to bind into SQL
- * @param $limit (int): LIMIT number
- * @param $offset (int): OFFSET number
- */
- public function runLimitQuery($sql, $args, $limit = 100, $offset = null)
- {
- $limitStr = $offsetStr = "";
-
- // construct LIMIT clause
- if (is_int($limit)) {
- $limitStr = " LIMIT {$limit}";
-
- // construct OFFSET clause (when a LIMIT pre-exists)
- if (is_int($offset)) {
- $offsetStr =" OFFSET {$offset}";
- }
- }
-
- $sql = $sql . $limitStr . $offsetStr;
-
- return $this->runQuery($sql, $args);
- }
-
-
- /** insert
- * @param $table (string): name of table
- * @param $values: an associative of (fieldName => value) pairs
- *
- * example call:
- * ---
- * $db->insert('products',
- * [
- * 'title' => 'My Dark Chocolate 200g',
- * 'text' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit ...',
- * 'isFood' => 1,
- * 'isToxic' => 0
- * ]
- * );
- *
- * ...which prepares the SQL query:
- * INSERT INTO products (title, text, isFood, isToxic)
- * VALUES (:title, :text, :isFood, :isToxic)
- *
- * ...and injects the values: [ :title => 'My Dark Chocolate 200g' , ... ]
- */
- public function insert($table, array $values)
- {
- $fieldSets = [];
- $valueSets = [];
- $bindSets = [];
-
- foreach($values as $key => $val) {
- $fieldSets[] = $key;
- $valueSets[] =':'. $key;
- $bindSets[':'. $key] = $val;
- }
-
- $sql = "INSERT INTO {$table} (". implode(', ', $fieldSets) .")
- VALUES (". implode(', ', $valueSets) .")";
-
- return $this->runQuery($sql, $bindSets);
- }
-
-
- /** update
- * @param $table (string): name of table
- * @param $values: an associative of (fieldName => value) pairs
- * @param $id: an associative of (fieldName => value) index fields
- *
- * example call:
- * ---
- * $db->update('products',
- * [ 'title' => 'My Chocolate','isFood' => 1 ],
- * [ 'id' => 123 ]
- * );
- *
- * ...which prepares the SQL query:
- * UPDATE products SET `title` = :title, `isFood` = :isFood WHERE id = :id
- *
- * ...and injects: [':title'=> 'My Chocolate' , ':isFood'=> 1 , ':id'=> 123]
- */
- public function update( $table, array $values, array $identity)
- {
- $fieldSets = []; // array of field names
- $idSets = []; // array of data-holders
- $bindSets = []; // array of data-bindings
-
- foreach($values as $key => $val) {
- $fieldSets[] = "{$key} = :{$key}";
- $bindSets[':'. $key] = $val;
- }
-
- foreach($identity as $key => $val) {
- $idSets = "{$key} = :{$key}";
- $bindSets[':'. $key] = $val;
- }
-
- $sql = "UPDATE {$table} SET ". implode(', ', $fieldSets)
- ." WHERE ". implode(" AND ", $idSets);
-
- return $this->runQuery($sql, $bindsArray);
- }
-
-
- /** multiInsert( table, fields , values )
- * Construct a multiple-insert clause
- *
- * @param $table (string): name of table
- * @param $fields (array): array with field-names
- * @param $values (array): array of value-arrays
- *
- * example call:
- * ---
- * $db->multiInsert('order_products',
- * [ 'orderID', 'productID', 'unitPrice', 'quantity', 'note' ],
- * [
- * [ 124, 102030, 1.25, 5, '' ],
- * [ 124, 102040, 10.50, 2, 'some note about product #102040' ],
- * [ 124, 102050, 7.20, 3, '' ],
- * [ 124, 102060, 3.25, 1, 'some other note' ]
- * ]
- * );
- */
- public function multiInsert($table, array $fieldsArray, array $valuesArray)
- {
- if (count($fieldsArray) != count($valuesArray[0])) {
- throw new Exception('Fields and value arrays don\'t match.');
- }
-
- // setup fieldsSet
- // ex. "(Title, Price, Status)"
- $fieldsSet = ' (`'. implode(
- '`, `', // make sure fieldnames are not SQL-bound terms
- str_replace('`', '', $fieldsArray) // clean fieldnames
- ) .'`) ';
-
- // setup holders array and bind-values array
- // ex. "(:Title1, :Price1, :Status1), (:Title2, :Price2, :Status2), ...",
- $holdersArray = [];
- $bindsArray = [];
- $counter = 1;
- foreach($valuesArray as $key => $rowArray) {
- $rowHolders = [];
-
- foreach($itemArray as $key => $val) {
- $rowHolders = ':'. $fieldsArray[$key] . $counter;
- $bindsArray[ ':'. $fieldsArray[$key] . $counter ] = $val;
- }
- $holdersArray[] = '('. implode(', ', $rowHolders ) .')';
- $counter++;
- }
-
- $sql = "INSERT INTO {$table}" . $fieldsSet
- . ' VALUES '. impload(', ', $holdersArray);
-
- return $this->runQuery($sql, $bindsArray);
- }
-
-}
diff --git a/classes/Registry.php b/classes/Registry.php
deleted file mode 100644
index df012e9..0000000
--- a/classes/Registry.php
+++ /dev/null
@@ -1,129 +0,0 @@
-URL = $_SERVER['REQUEST_URI'];
- $this->PATH = (!empty($parsed['path'])) ? urldecode($parsed['path']) : '';
- $this->QUERY = (!empty($parsed['query'])) ? urldecode($parsed['query']) : false;
- $this->HOST = $_SERVER['HTTP_HOST'];
- $this->PORT = $_SERVER['SERVER_PORT'];
- $this->TIME = $_SERVER['REQUEST_TIME'];
- $this->CLI_IP = $_SERVER['REMOTE_ADDR'];
-
- $this->METHOD = strtolower($_SERVER['REQUEST_METHOD']);
-
- $this->GET = $_GET; // $_GET should only used
- // to request data or specify options (never to perform
- // system-changes) thus should not need any validation;
- // * If (for any reason) you requide $_GET sanitization
- // enable it later on the method's code
-
- // $this->INTERFACE = php_sapi_name();
-
- $this->AGENT = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
- $this->SIGNATURE = sha1(
- $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
- . $_SERVER['HTTP_ACCEPT'] ?? ''
- . $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''
- . $_SERVER['HTTP_ACCEPT_ENCODING'] ?? ''
- );
-
- // sanitize user input
- // if (isset($_GET)) { $this->GET = $this->sanitize($_GET); }
- $this->GET = $this->sanitize($_GET);
- if (isset($_POST)) { $this->POST = $this->sanitize($_POST); }
- if (isset($_COOKIE)) { $this->COOKIE = $this->sanitize($_COOKIE); }
-
- // check anti-CSRF token if needed
- // (again, GET requests should not need CSRF cheking)
- if (in_array($this->METHOD, ['post', 'put', 'patch', 'delete'])) {
- // TODO: only if CSRF protection enabled...
- $this->checkCsrfToken();
- }
-
- }
-
-
- private function sanitize($array)
- {
- // TODO:
- // ...
- return $array;
- }
-
-
- public function checkCsrfToken()
- {
- // TODO:
- // ...
- // if SCRF-token is not valideted, serve 403
- return $array;
- }
-
-}
-
diff --git a/classes/Route.php b/classes/Route.php
deleted file mode 100644
index 072723c..0000000
--- a/classes/Route.php
+++ /dev/null
@@ -1,97 +0,0 @@
- $expression,
- 'function' => $function,
- 'method' => strtolower($method)
- ));
- }
-
-
- /** notFound($function)
- * ---
- * @param $function : call back function to be executed
- */
- public static function notFound($function)
- {
- self::$notFound = $function;
- }
-
-
- /** run()
- * ---
- * Parse request ; Find mathing route ;
- * then call route's function
- * usualy a Controller::method([poarametres])
- */
- public static function run(Request $request)
- {
- // $request = Registry::get('REQUEST');
- $path = $request->PATH; // request path
- $method = $request->METHOD; // request method
-
- $path_match_found = false;
- $route_match_found = false;
-
- foreach(self::$routes as $route) {
-
- // If method matched check the path
- if ($route['method'] == $method || $method == 'any') {
-
- // Add 'find string start' automatically
- $route['expression'] = '^'.$route['expression'];
-
- // Add 'find string end' automatically
- $route['expression'] = $route['expression'].'$';
-
- // Check path match
- if (preg_match('#'. $route['expression'] .'#', $path, $matches)) {
-
- $route_match_found = true;
-
- array_shift($matches); // Always remove first element. This contains the whole string
-
- call_user_func_array($route['function'], $matches);
- break; // Do not check other routes
-
- }
- }
- }
-
- // No matching route was found
- if (!$route_match_found) {
- header("HTTP/1.0 404 Not Found");
- if (self::$notFound) {
- call_user_func_array(self::$notFound, []);
- }
- }
-
- }
-
-}
diff --git a/classes/Security.php b/classes/Security.php
deleted file mode 100644
index 61979db..0000000
--- a/classes/Security.php
+++ /dev/null
@@ -1,923 +0,0 @@
-', '<', '>',
- "'", '"', '&', '$', '#',
- '{', '}', '[', ']', '=',
- ';', '?', '%20', '%22',
- '%3c', // <
- '%253c', // <
- '%3e', // >
- '%0e', // >
- '%28', // (
- '%29', // )
- '%2528', // (
- '%26', // &
- '%24', // $
- '%3f', // ?
- '%3b', // ;
- '%3d' // =
- );
-
- public $charset = 'UTF-8'; // Character set @var (string) Will be overridden by the constructor.
-
- protected $_xss_hash; // XSS Hash: @var (string) Random Hash for protecting URLs.
-
- protected $_csrf_hash; // CSRF Hash: @var (string) Random hash for Cross Site Request Forgery protection cookie
-
- // CSRF Expire time @var (int)
- // Expiration time for Cross Site Request Forgery protection cookie.
- protected $_csrf_expire = 7200; // defaults to 2hours (=7200 seconds)
-
- // CSRF Token name: @var (string) Token name for Cross Site Request Forgery protection cookie.
- protected $_csrf_token_name = 'pi_csrf_token';
-
- // CSRF Cookie name: @var (string) Cookie name for Cross Site Request Forgery protection cookie.
- protected $_csrf_cookie_name = 'pi_csrf_token';
-
- // List of never allowed strings @var (array)
- protected $_never_allowed_str = array(
- 'document.cookie' => '[removed]',
- '(document).cookie' => '[removed]',
- 'document.write' => '[removed]',
- '(document).write' => '[removed]',
- '.parentNode' => '[removed]',
- '.innerHTML' => '[removed]',
- '-moz-binding' => '[removed]',
- '' => '-->',
- ' '<![CDATA[',
- '' => '<comment>',
- '<%' => '<%'
- );
-
- // List of never allowed regex replacements @var (array)
- protected $_never_allowed_regex = array(
- 'javascript\s*:',
- '(\(?document\)?|\(?window\)?(\.document)?)\.(location|on\w*)',
- 'expression\s*(\(|&\#40;)', // CSS and IE
- 'vbscript\s*:', // IE, surprise!
- 'wscript\s*:', // IE
- 'jscript\s*:', // IE
- 'vbs\s*:', // IE
- 'Redirect\s+30\d',
- "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?"
- );
-
-
- /** Class constructor
- * ---
- * @return void
- */
- public function __construct($charset = 'UTF-8')
- {
- $this->charset = $charset;
-
- /* ---
- // Is CSRF protection enabled?
- if (CSRF_PROTCTION') && ! is_cli())
- { --*/
- /**************
- // CSRF config
- foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key)
- {
- //// if (NULL !== ($val = config_item($key)))
- //// {
- $this->{'_'.$key} = $val;
- //// }
- }
-
- // Append application specific cookie prefix
- //// if ($cookie_prefix = config_item('cookie_prefix'))
- //// {
- $this->_csrf_cookie_name = $cookie_prefix . $this->_csrf_cookie_name;
- //// }
-
- // Set the CSRF hash
- $this->_csrf_set_hash();
- $this->csrf_verify();
- ***************/
- /* ---
- } --*/
-
- //// log_message('info', 'Security Class Initialized');
- }
-
-
- /** CSRF Verify
- * ---
- * @return 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
- $valid = isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])
- && is_string($_POST[$this->_csrf_token_name]) && is_string($_COOKIE[$this->_csrf_cookie_name])
- && hash_equals($_POST[$this->_csrf_token_name], $_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;
- }
-
-
- /** CSRF Set Cookie
- * ---
- * @codeCoverageIgnore
- * @return Security
- */
- public function csrf_set_cookie()
- {
- $expire = time() + $this->_csrf_expire;
- $secure_cookie = (bool) config_item('cookie_secure');
-
- if ($secure_cookie && ! is_https())
- {
- return FALSE;
- }
-
- // php7.3+
- setcookie(
- $this->_csrf_cookie_name,
- $this->_csrf_hash,
- array(
- 'expires' => $expire,
- 'path' => config_item('cookie_path'),
- 'domain' => config_item('cookie_domain'),
- 'secure' => $secure_cookie,
- 'httponly' => config_item('cookie_httponly'),
- 'samesite' => 'Strict'
- )
- );
-
- log_message('info', 'CSRF cookie sent');
-
- return $this;
- }
-
-
- /** Show CSRF Error
- * --
- * @return void
- */
- public function csrf_show_error()
- {
- show_error('The action you have requested is not allowed.', 403);
- }
-
-
- /** Get CSRF Hash
- * ---
- * @see CI_Security::$_csrf_hash
- * @return string CSRF hash
- */
- public function get_csrf_hash()
- {
- return $this->_csrf_hash;
- }
-
-
- /** Get CSRF Token Name
- * ---
- * @see CI_Security::$_csrf_token_name
- * @return string CSRF token name
- */
- public function get_csrf_token_name()
- {
- return $this->_csrf_token_name;
- }
-
-
- /** XSS Clean
- * ---
- * Sanitizes data so that Cross Site Scripting Hacks can be
- * prevented. This method does a fair amount of work but
- * it is extremely thorough, designed to prevent even the
- * most obscure XSS attempts. Nothing is ever 100% foolproof,
- * of course, but I haven't been able to get anything passed
- * the filter.
- *
- * Note: Should only be used to deal with data upon submission.
- * It's not something that should be used for general
- * runtime processing.
- *
- * @link http://channel.bitflux.ch/wiki/XSS_Prevention
- * Based in part on some code and ideas from Bitflux.
- *
- * @link http://ha.ckers.org/xss.html
- * To help develop this script I used this great list of
- * vulnerabilities along with a few other hacks I've
- * harvested from examining vulnerabilities in other programs.
- *
- * @param string|string[] $str Input data
- * @param bool $is_image Whether the input is an image
- * @return string
- */
- public function xss_clean($str, $is_image = FALSE)
- {
- // Is the string an array?
- if (is_array($str))
- {
- foreach ($str as $key => &$value)
- {
- $str[$key] = $this->xss_clean($value);
- }
-
- return $str;
- }
-
- // Remove Invisible Characters
- $str = remove_invisible_characters($str);
-
- // URL Decode
- // Just in case stuff like this is submitted:
- // Google
- // NOTE: Use rawurldecode() so it does not remove plus signs
- if (stripos($str, '%') !== false)
- {
- do
- {
- $oldstr = $str;
- $str = rawurldecode($str);
- $str = preg_replace_callback('#%(?:\s*[0-9a-f]){2,}#i', array($this, '_urldecodespaces'), $str);
- }
- while ($oldstr !== $str);
- unset($oldstr);
- }
-
- // Convert character entities to ASCII
- // ---
- // This permits our tests below to work reliably.
- // We only convert entities that are within tags since
- // these are the ones that will pose security problems.
- $str = preg_replace_callback("/[^a-z0-9>]+[a-z0-9]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str);
- $str = preg_replace_callback('/<\w+.*/si', array($this, '_decode_entity'), $str);
-
- // Remove Invisible Characters Again!
- $str = remove_invisible_characters($str);
-
-
- // Convert all tabs to spaces
- // ---
- // This prevents strings like this: ja vascript
- // NOTE: we deal with spaces between characters later.
- // NOTE: preg_replace was found to be amazingly slow here on
- // large blocks of data, so we use str_replace.
- $str = str_replace("\t", ' ', $str);
-
- // Capture converted string for later comparison
- $converted_string = $str;
-
- // Remove Strings that are never allowed
- $str = $this->_do_never_allowed($str);
-
-
- // Makes PHP tags safe
- // NOTE: XML tags are inadvertently replaced too:
- // '), array('<?', '?>'), $str);
- }
-
- // Compact any exploded words
- // ---
- // This corrects words like: j a v a s c r i p t
- // These words are compacted back to their correct state.
- $words = array(
- 'javascript', 'expression', 'vbscript', 'jscript', 'wscript',
- 'vbs', 'script', 'base64', 'applet', 'alert', 'document',
- 'write', 'cookie', 'window', 'confirm', 'prompt', 'eval'
- );
-
- foreach ($words as $word)
- {
- $word = implode('\s*', str_split($word)).'\s*';
-
- // We only want to do this when it is followed by a non-word character
- // That way valid stuff like "dealer to" does not become "dealerto"
- $str = preg_replace_callback('#('.substr($word, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str);
- }
-
-
- // Remove disallowed Javascript in links or img tags
- // We used to do some version comparisons and use of stripos(),
- // but it is dog slow compared to these simplified non-capturing
- // preg_match(), especially if the pattern exists in the string
- //
- // Note: It was reported that not only space characters, but all in
- // the following pattern can be parsed as separators between a tag name
- // and its attributes: [\d\s"\'`;,\/\=\(\x00\x0B\x09\x0C]
- // ... however, remove_invisible_characters() above already strips the
- // hex-encoded ones, so we'll skip them below.
- do
- {
- $original = $str;
-
- if (preg_match('/]+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str);
- }
-
- if (preg_match('/
]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str);
- }
-
- if (preg_match('/script|xss/i', $str))
- {
- $str = preg_replace('#*(?:script|xss).*?>#si', '[removed]', $str);
- }
- }
- while ($original !== $str);
- unset($original);
-
-
- // Sanitize naughty HTML elements
- // ---
- // If a tag containing any of the words in the list
- // below is found, the tag gets converted to entities.
- // So this: