Overview

Packages

  • CONTENIDO
  • Core
    • Authentication
    • Backend
    • Cache
    • CEC
    • Chain
    • ContentType
    • Database
    • Debug
    • Exception
    • Frontend
      • Search
      • URI
      • Util
    • GenericDB
      • Model
    • GUI
      • HTML
    • I18N
    • LayoutHandler
    • Log
    • Security
    • Session
    • Util
    • Validation
    • Versioning
    • XML
  • Module
    • ContentSitemapHtml
    • ContentSitemapXml
    • ContentUserForum
    • NavigationTop
    • ScriptCookieDirective
  • mpAutoloaderClassMap
  • None
  • PHP
  • Plugin
    • ContentAllocation
    • CronjobOverview
    • FormAssistant
    • FrontendLogic
    • FrontendUsers
    • Linkchecker
    • ModRewrite
    • Newsletter
    • Repository
      • FrontendNavigation
      • KeywordDensity
    • SmartyWrapper
    • UrlShortener
    • UserForum
    • Workflow
  • PluginManager
  • Setup
    • Form
    • GUI
    • Helper
      • Environment
      • Filesystem
      • MySQL
      • PHP
    • UpgradeJob

Classes

  • cRequestValidator
  • cSecurity
  • cUpdateNotifier
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: /**
  3:  * This file contains the the request validator class.
  4:  *
  5:  * @package    Core
  6:  * @subpackage Security
  7:  * @author     Mischa Holz
  8:  * @author     Andreas Kummer
  9:  * @copyright  four for business AG <www.4fb.de>
 10:  * @license    http://www.contenido.org/license/LIZENZ.txt
 11:  * @link       http://www.4fb.de
 12:  * @link       http://www.contenido.org
 13:  */
 14: 
 15: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
 16: 
 17: /**
 18:  * Class to check get and post variables
 19:  *
 20:  * @package    Core
 21:  * @subpackage Security
 22:  */
 23: class cRequestValidator {
 24: 
 25:     /**
 26:      * Instance of this class.
 27:      *
 28:      * @var cRequestValidator
 29:      */
 30:     private static $_instance = null;
 31: 
 32:     /**
 33:      * Path and filename of logfile.
 34:      *
 35:      * @var string
 36:      */
 37:     protected $_logPath;
 38: 
 39:     /**
 40:      * Flag whether to write log or not.
 41:      *
 42:      * @var bool
 43:      */
 44:     protected $_log = true;
 45: 
 46:     /**
 47:      * Path to config file.
 48:      *
 49:      * @var string
 50:      */
 51:     protected $_configPath;
 52: 
 53:     /**
 54:      * Array with all possible parameters and parameter formats.
 55:      * Structure has to be:
 56:      * <code>
 57:      * $check['GET']['param1'] = VALIDATE_FORMAT;
 58:      * $check['POST']['param2'] = VALIDATE_FORMAT;
 59:      * </code>
 60:      * Possible formats are defined as constants in top of these class file.
 61:      *
 62:      * @var array
 63:      */
 64:     protected $_check = array();
 65: 
 66:     /**
 67:      * Array with forbidden parameters.
 68:      * If any of these is set the request will be invalid.
 69:      *
 70:      * @var array
 71:      */
 72:     protected $_blacklist = array();
 73: 
 74:     /**
 75:      * Contains first invalid parameter name.
 76:      *
 77:      * @var string
 78:      */
 79:     protected $_failure = '';
 80: 
 81:     /**
 82:      * Current mode.
 83:      *
 84:      * @var string
 85:      */
 86:     protected $_mode = '';
 87: 
 88:     /**
 89:      * Regexp for integers.
 90:      *
 91:      * @var string
 92:      */
 93:     const CHECK_INTEGER = '/^[0-9]*$/';
 94: 
 95:     /**
 96:      * Regexp for primitive strings.
 97:      *
 98:      * @var string
 99:      */
100:     const CHECK_PRIMITIVESTRING = '/^[a-zA-Z0-9 -_]*$/';
101: 
102:     /**
103:      * Regexp for strings.
104:      *
105:      * @var string
106:      */
107:     const CHECK_STRING = '/^[\w0-9 -_]*$/';
108: 
109:     /**
110:      * Regexp for 32 character hash.
111:      *
112:      * @var string
113:      */
114:     const CHECK_HASH32 = '/^[a-zA-Z0-9]{32}$/';
115: 
116:     /**
117:      * Regexp for valid belang values.
118:      *
119:      * @var string
120:      */
121:     const CHECK_BELANG = '/^[a-z]{2}_[A-Z]{2}$/';
122: 
123:     /**
124:      * Regexp for valid area values.
125:      *
126:      * @var string
127:      */
128:     const CHECK_AREASTRING = '/^[a-zA-Z_]*$/';
129: 
130:     /**
131:      * Regexp for validating file upload paths.
132:      *
133:      * @var string
134:      */
135:     const CHECK_PATHSTRING = '!([*]*\/)|(dbfs:\/[*]*)|(dbfs:)|(^)$!';
136: 
137:     /**
138:      * Constructor to create an instance of this class.
139:      * The constructor sets up the singleton object and reads the config from
140:      *     'data/config/' . CON_ENVIRONMENT . '/config.http_check.php'
141:      * It also reads existing local config from
142:      *     'data/config/' . CON_ENVIRONMENT . '/config.http_check.local.php'
143:      *
144:      * @throws cFileNotFoundException if the configuration can not be loaded
145:      */
146:     private function __construct() {
147: 
148:         // globals from config.http_check.php file which is included below
149:         global $bLog, $sMode, $aCheck, $aBlacklist;
150: 
151:         // some paths...
152:         $installationPath = str_replace('\\', '/', realpath(dirname(__FILE__) . '/../..'));
153:         $configPath       = $installationPath . '/data/config/' . CON_ENVIRONMENT;
154: 
155:         $this->_logPath = $installationPath . '/data/logs/security.txt';
156: 
157:         // check config and logging path
158:         if (cFileHandler::exists($configPath . '/config.http_check.php')) {
159:             $this->_configPath = $configPath;
160:         } else {
161:             throw new cFileNotFoundException('Could not load cRequestValidator configuration! (invalid path) ' . $configPath . '/config.http_check.php');
162:         }
163: 
164:         // include configuration
165:         require($this->_configPath . '/config.http_check.php');
166: 
167:         // if custom config exists, include it also here
168:         if (cFileHandler::exists($this->_configPath . '/config.http_check.local.php')) {
169:             require($this->_configPath . '/config.http_check.local.php');
170:         }
171: 
172:         $this->_log  = $bLog;
173:         $this->_mode = $sMode;
174: 
175:         if ($this->_log === true) {
176:             if (empty($this->_logPath) || !is_writeable(dirname($this->_logPath))) {
177:                 $this->_log = false;
178:             }
179:         }
180: 
181:         $this->_check = $aCheck;
182:         foreach ($aBlacklist as $elem) {
183:             $this->_blacklist[] = cString::toLowerCase($elem);
184:         }
185:     }
186: 
187:     /**
188:      * Returns the instance of this class.
189:      *
190:      * @return cRequestValidator
191:      */
192:     public static function getInstance() {
193: 
194:         if (self::$_instance === null) {
195:             self::$_instance = new self();
196:         }
197: 
198:         return self::$_instance;
199:     }
200: 
201:     /**
202:      * Checks every given parameter.
203:      * Parameters which aren't defined in config.http_check.php
204:      * are considered to be fine.
205:      *
206:      * @return bool
207:      *         True if every parameter is fine
208:      *
209:      * @throws cInvalidArgumentException
210:      */
211:     public function checkParams() {
212: 
213:         if ((!$this->checkGetParams()) || (!$this->checkPostParams() || (!$this->checkCookieParams()))) {
214:             $this->logHackTrial();
215: 
216:             if ($this->_mode == 'stop') {
217:                 ob_end_clean();
218:                 $msg = 'Parameter check failed! (%s = %s %s %s)';
219:                 // prevent XSS!
220:                 $msg = sprintf($msg, htmlentities($this->_failure), htmlentities($_GET[$this->_failure]), htmlentities($_POST[$this->_failure]), htmlentities($_COOKIE[$this->_failure]));
221:                 die($msg);
222:             }
223:         }
224: 
225:         return true;
226:     }
227: 
228:     /**
229:      * Checks GET parameters only.
230:      *
231:      * @see    cRequestValidator::checkParams()
232:      * @return bool
233:      *         True if every parameter is fine
234:      */
235:     public function checkGetParams() {
236: 
237:         return $this->checkArray($_GET, 'GET');
238:     }
239: 
240:     /**
241:      * Checks POST parameters only.
242:      *
243:      * @see    cRequestValidator::checkParams()
244:      * @return bool
245:      *         True if every parameter is fine
246:      */
247:     public function checkPostParams() {
248: 
249:         return $this->checkArray($_POST, 'POST');
250:     }
251: 
252:     /**
253:      * Checks COOKIE parameters only.
254:      *
255:      * @see    cRequestValidator::checkParams()
256:      * @return bool
257:      *         True if every parameter is fine
258:      */
259:     public function checkCookieParams() {
260: 
261:         return $this->checkArray($_COOKIE, 'COOKIE');
262:     }
263: 
264: 
265:     /**
266:      * Checks a single parameter.
267:      *
268:      * @see cRequestValidator::checkParams()
269:      *
270:      * @param string $type
271:      *         GET or POST
272:      * @param string $key
273:      *         the key of the parameter
274:      * @param mixed  $value
275:      *         the value of the parameter
276:      *
277:      * @return bool
278:      *         True if the parameter is fine
279:      */
280:     public function checkParameter($type, $key, $value) {
281: 
282:         $result = false;
283: 
284:         if (in_array(cString::toLowerCase($key), $this->_blacklist)) {
285:             return false;
286:         }
287: 
288:         if (in_array(cString::toUpperCase($type), array(
289:             'GET',
290:             'POST',
291:             'COOKIE'
292:         ))) {
293:             if (!isset($this->_check[$type][$key]) && (is_null($value) || empty($value))) {
294:                 // if unknown but empty the value is unaesthetic but ok
295:                 $result = true;
296:             } elseif (isset($this->_check[$type][$key])) {
297:                 // parameter is known, check it...
298:                 $result = preg_match($this->_check[$type][$key], $value);
299:             } else {
300:                 // unknown parameter. Will return true
301:                 $result = true;
302:             }
303:         }
304: 
305:         return $result;
306:     }
307: 
308:     /**
309:      * Returns the first bad parameter.
310:      *
311:      * @return string
312:      *         the key of the bad parameter
313:      */
314:     public function getBadParameter() {
315: 
316:         return $this->_failure;
317:     }
318: 
319:     /**
320:      * Writes a log entry containing information about the request which
321:      * led to the halt of the execution.
322:      *
323:      * @throws cInvalidArgumentException
324:      */
325:     protected function logHackTrial() {
326: 
327:         if ($this->_log === true && !empty($this->_logPath)) {
328:             $content = date('Y-m-d H:i:s') . '    ';
329:             $content .= $_SERVER['REMOTE_ADDR'] . str_repeat(' ', 17 - cString::getStringLength($_SERVER['REMOTE_ADDR'])) . "\n";
330:             $content .= '    Query String: ' . $_SERVER['QUERY_STRING'] . "\n";
331:             $content .= '    Bad parameter: ' . $this->getBadParameter() . "\n";
332:             $content .= '    POST array: ' . print_r($_POST, true) . "\n";
333:             $content .= '    GET array: ' . print_r($_GET, true) . "\n";
334:             $content .= '    COOKIE array: ' . print_r($_COOKIE, true) . "\n";
335:             cFileHandler::write($this->_logPath, $content, true);
336:         } elseif ($this->_mode == 'continue') {
337:             echo "\n<br>VIOLATION: URL contains invalid or undefined paramaters! URL: '" . conHtmlentities($_SERVER['QUERY_STRING']) . "' <br>\n";
338:         }
339:     }
340: 
341:     /**
342:      * This function removes unwished chars from given string
343:      *
344:      * @param string $param
345:      *
346:      * @return string
347:      */
348:     public static function cleanParameter($param) {
349: 
350:         $charsToReplace = array(
351:             '<', '>', '?', '&', '$', '{', '}', '(', ')'
352:         );
353: 
354:         foreach ($charsToReplace as $char) {
355:             $param = str_replace($char, '', $param);
356:         }
357: 
358:         return $param;
359:     }
360: 
361:     /**
362:      * Checks an array for validity.
363:      *
364:      * @param array  $arr
365:      *         the array which has to be checked
366:      * @param string $type
367:      *         GET or POST
368:      *
369:      * @return bool
370:      *         true if everything is fine.
371:      */
372:     protected function checkArray($arr, $type) {
373: 
374:         $result = true;
375: 
376:         foreach ($arr as $key => $value) {
377:             if (!$this->checkParameter(cString::toUpperCase($type), $key, $value)) {
378:                 $this->_failure = $key;
379:                 $result         = false;
380:                 break;
381:             }
382:         }
383: 
384:         return $result;
385:     }
386: 
387: }
388: 
CMS CONTENIDO 4.10.0 API documentation generated by ApiGen 2.8.0