From: Pádraic B. <mau...@ho...> - 2005-09-08 11:30:28
|
All, Another set of documentation for yet another class. Posting this to both projects since it concerns use within PHP game code. Any queries, comments, or suggestions - send them my way ;). In short this describes an InputFilter class, for use in pre-processing POST/GET/COOKIE data. Should make some people feel a little safer. This now brings to two the number of security levels in place to prevent/discourage cheating (the other being the URL Authorisation, i.e. the Navigation Class). Paidi Input Filtering Class ===================== Author: Padraic Brady Date: 8 Sept 2005 Revision: 1.0 Type: Documentation Contents -------- 1. Introduction 2. Duties of an Input Filter 3. Operation of Input Filter 4. Source Code (v0.1) 5. Future Considerations 1. Introduction --------------- One of the major shortfalls in all SR/QS code to date (perhaps not Q-Site) is that they continually ignore implementing a clear specific method of filtering input, and escaping data. It's one of the shortfalls identified in my May review of QS (along with the interlinked OO design, memory usage, and global objects - there are others). The basis of Input Filtering is simply that you cannot under any circumstances trust any data sourced by any user. This extends to GET, POST, COOKIE and some elements of the SERVER superglobals. At present all input data is subject to testing on the data we expect to receive - this ignores any possible other data which applications may not expect. Even the testing is largely ad-hoc, and may or may not check on data type. To resolve this problem, the Q_InputFilter class has been written (this is not complete as yet, but it gives some idea of how it works. 2. Duties of an Input Filter ---------------------------- Full complete input filtering at the end of the day, is going to done by client code. However the primary steps can be retained in the Q_InputFilter class. These duties include: - Deletion of unexpected data - Checking of data type/content - Overwriting of Superglobal arrays 3. Operation of an Input Filter ------------------------------- Simply put, we start comparing input data to a template of what any one particular part of our application expects. This typically involves a whitelist approach (since attempting to blacklist potential problems would be a hugely unreliable approach). The developer basically writes their code, then adds a template (a class) which specifies exactly the keys and value type/content of the input data the script expects. What should happen, is that any input data which breaks these pre-defined rules will immediately result in two outcomes: a. The data is deleted b. The data (and user) get logged In summary: 1. Create a template of all expected input data (a whitelist) 2. Search the Superglobals for unexpected data 3. Delete the unexpected data 4. Log any unexpected data, and the origin of that data 4. Source Code (v0.1) --------------------- As the class is still in writing, don't view this as a final version. I also don't claim credit for the process, which is based on a set of articles by Ben Ramsey. The example below uses the QS3 location.php base file in showing a template. The InputFilterData Class (for location.php): <?php /* * @copyright © 2005 Padraic Brady */ class Location_InputFilterData { var $post; var $get; function Location_InputFilterData() { $this->post = array( 'ss_op'=>'string' ); $this->get = array( 'toloc'=>'integer', 'fid'=>'integer' ); } function getPost() { return $this->post; } function getGet() { return $this->get; } } ?> Typically (under php5) the properties of this class would be type 'private'. Hence the use of getters. The above shows a simple definition of expected input data, it includes the expected key names, the format of the data, and differentiates between POST/GET/COOKIE. It does not include $_REQUEST (I prefer not using it). If any user were to pass a GET value of $_GET['admin'], this would be found by the InputFilter class (below) and since it does not match the whitelist of expected GET keys, it would be deleted. Similarly, if a GET value for ('fid') of 'gohome' were sent, it too would be deleted, since as above our template file indicates that $_GET['fid'] should be an number. (ed: use of integer above is not entirely accurate.) In this way, if we don't expect a value, it will not survive passage through the filter. The Q_InputFilter Class looks like (for the moment): <?php /* * @copyright © 2005 Padraic Brady */ class Q_InputFilter { // constructor (not to be instantiated directly) function Q_InputFilter() { $error = new Q_FatalError(debug_backtrace()); $error->reportError('The Q_InputFilter() Class cannot be directly instantiated.'); } // static function function filter(&$input, $allowed) { $filtered = array(); foreach ($input as $key => $value) { if (array_key_exists($key, $allowed)) { $keyset = (isset($allowed[$key]) && !empty($allowed[$key])) ? true : false; switch ($allowed[$key]) { case 'string': $value = (ctype_print($value)) ? $value : null; break; case 'integer': $value = (ctype_digit($value)) ? $value : null; break; case 'alphanumeric': $value = (ctype_alnum($value)) ? $value : null; break; case 'alphabetic': $value = (ctype_alpha($value)) ? $value : null; break; case 'lowercase': $value = (ctype_lower($value)) ? $value : null; break; case 'uppercase': $value = (ctype_upper($value)) ? $value : null; break; case 'nospaces': $value = (ctype_graph($value)) ? $value : null; break; default: $error = new Q_FatalError(debug_backtrace()); $error->reportError('Invalid option set for the value type of a $allowed array parameter value. The type of any expected input data key must be one of: string, integer, alphanumeric, alphabetic, lowercase, uppercase or nospaces.'); } $filtered[$key] = $value; if($keyset === true && $value == '') { // in this case, a non-empty value existed for a key, but was blanked by the filter // this may indicate user intentionally passed own data - should be logged to Audit Trail } } } unset($input); return $filtered; } } The above class contains a simple static method for searching a superglobal array. It should be noted that the InputFilter is the first item to be called prior to including any other code, e.g. the initiation file for QS3, core.inc.php. It's use for say location.php would be as folows: <?php require_once('inputfilters/Location.php'); $filterdata = new Location_InputFilterData(); // this section would typically be included in core.inc.php (done without specifying in client code) require_once('../qlib/classes/inputfilter/InputFilter.php'); $_GET = Q_InputFilter::filter($_GET, $filterdata->getGet()); $_POST = Q_InputFilter::filter($_POST, $filterdata->getPost()); require_once('core.inc.php'); // ... other file code ... ?> 5. Future Considerations ------------------------ At present the class is limited to applying logic on key names, and value types. It is being planned to add certain specialised cases, e.g. email, username, url, publictext, etc. It is also expected that the class will at least partially integrate with the htmlSafe library to enable checking of certain data input for XSS and other vulnerabilies. The final version will also integrate with the Q_Error, and Q_Log classes, which will enable the above error reporting, and the logging of unexpected input data (makes sense to keep track of potential cheaters or scriptkiddies). Also, another objective is to expand on the data checking to include string lengths, integer max/min value, etc. Although these will still largely be checked by the client code. The class is meant as a set of standard pre-processing tasks - further input checking will obviously be necessary that is specific to the code at hand. -|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|- Pádraic Brady aka Maugrim The Reaper http://www.quantum-star.com/ http://www.shadowsrising.net/ -|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|- |