/lenasys/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/lenasys/trunk
20.1.1 by galaxyAbstractor
* Added an simple admin panel to the codeviewer-cmssy stuff
1
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP 5.1.6 or newer
6
 *
7
 * @package		CodeIgniter
8
 * @author		ExpressionEngine Dev Team
9
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
10
 * @license		http://codeigniter.com/user_guide/license.html
11
 * @link		http://codeigniter.com
12
 * @since		Version 1.0
13
 * @filesource
14
 */
15
16
// ------------------------------------------------------------------------
17
18
/**
19
 * Security Class
20
 *
21
 * @package		CodeIgniter
22
 * @subpackage	Libraries
23
 * @category	Security
24
 * @author		ExpressionEngine Dev Team
25
 * @link		http://codeigniter.com/user_guide/libraries/security.html
26
 */
27
class CI_Security {
28
29
	/**
30
	 * Random Hash for protecting URLs
31
	 *
32
	 * @var string
33
	 * @access protected
34
	 */
35
	protected $_xss_hash			= '';
36
	/**
37
	 * Random Hash for Cross Site Request Forgery Protection Cookie
38
	 *
39
	 * @var string
40
	 * @access protected
41
	 */
42
	protected $_csrf_hash			= '';
43
	/**
44
	 * Expiration time for Cross Site Request Forgery Protection Cookie
45
	 * Defaults to two hours (in seconds)
46
	 *
47
	 * @var int
48
	 * @access protected
49
	 */
50
	protected $_csrf_expire			= 7200;
51
	/**
52
	 * Token name for Cross Site Request Forgery Protection Cookie
53
	 *
54
	 * @var string
55
	 * @access protected
56
	 */
57
	protected $_csrf_token_name		= 'ci_csrf_token';
58
	/**
59
	 * Cookie name for Cross Site Request Forgery Protection Cookie
60
	 *
61
	 * @var string
62
	 * @access protected
63
	 */
64
	protected $_csrf_cookie_name	= 'ci_csrf_token';
65
	/**
66
	 * List of never allowed strings
67
	 *
68
	 * @var array
69
	 * @access protected
70
	 */
71
	protected $_never_allowed_str = array(
72
		'document.cookie'	=> '[removed]',
73
		'document.write'	=> '[removed]',
74
		'.parentNode'		=> '[removed]',
75
		'.innerHTML'		=> '[removed]',
76
		'window.location'	=> '[removed]',
77
		'-moz-binding'		=> '[removed]',
78
		'<!--'				=> '&lt;!--',
79
		'-->'				=> '--&gt;',
80
		'<![CDATA['			=> '&lt;![CDATA[',
81
		'<comment>'			=> '&lt;comment&gt;'
82
	);
83
84
	/* never allowed, regex replacement */
85
	/**
86
	 * List of never allowed regex replacement
87
	 *
88
	 * @var array
89
	 * @access protected
90
	 */
91
	protected $_never_allowed_regex = array(
92
		'javascript\s*:',
93
		'expression\s*(\(|&\#40;)', // CSS and IE
94
		'vbscript\s*:', // IE, surprise!
95
		'Redirect\s+302',
96
		"([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?"
97
	);
98
99
	/**
100
	 * Constructor
101
	 *
102
	 * @return	void
103
	 */
104
	public function __construct()
105
	{
106
		// Is CSRF protection enabled?
107
		if (config_item('csrf_protection') === TRUE)
108
		{
109
			// CSRF config
110
			foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key)
111
			{
112
				if (FALSE !== ($val = config_item($key)))
113
				{
114
					$this->{'_'.$key} = $val;
115
				}
116
			}
117
118
			// Append application specific cookie prefix
119
			if (config_item('cookie_prefix'))
120
			{
121
				$this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name;
122
			}
123
124
			// Set the CSRF hash
125
			$this->_csrf_set_hash();
126
		}
127
128
		log_message('debug', "Security Class Initialized");
129
	}
130
131
	// --------------------------------------------------------------------
132
133
	/**
134
	 * Verify Cross Site Request Forgery Protection
135
	 *
136
	 * @return	object
137
	 */
138
	public function csrf_verify()
139
	{
140
		// If it's not a POST request we will set the CSRF cookie
141
		if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
142
		{
143
			return $this->csrf_set_cookie();
144
		}
145
146
		// Do the tokens exist in both the _POST and _COOKIE arrays?
147
		if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]))
148
		{
149
			$this->csrf_show_error();
150
		}
151
152
		// Do the tokens match?
153
		if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
154
		{
155
			$this->csrf_show_error();
156
		}
157
158
		// We kill this since we're done and we don't want to
159
		// polute the _POST array
160
		unset($_POST[$this->_csrf_token_name]);
161
162
		// Nothing should last forever
163
		unset($_COOKIE[$this->_csrf_cookie_name]);
164
		$this->_csrf_set_hash();
165
		$this->csrf_set_cookie();
166
167
		log_message('debug', 'CSRF token verified');
168
169
		return $this;
170
	}
171
172
	// --------------------------------------------------------------------
173
174
	/**
175
	 * Set Cross Site Request Forgery Protection Cookie
176
	 *
177
	 * @return	object
178
	 */
179
	public function csrf_set_cookie()
180
	{
181
		$expire = time() + $this->_csrf_expire;
182
		$secure_cookie = (config_item('cookie_secure') === TRUE) ? 1 : 0;
183
184
		if ($secure_cookie && (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off'))
185
		{
186
			return FALSE;
187
		}
188
189
		setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie);
190
191
		log_message('debug', "CRSF cookie Set");
192
193
		return $this;
194
	}
195
196
	// --------------------------------------------------------------------
197
198
	/**
199
	 * Show CSRF Error
200
	 *
201
	 * @return	void
202
	 */
203
	public function csrf_show_error()
204
	{
205
		show_error('The action you have requested is not allowed.');
206
	}
207
208
	// --------------------------------------------------------------------
209
210
	/**
211
	 * Get CSRF Hash
212
	 *
213
	 * Getter Method
214
	 *
215
	 * @return 	string 	self::_csrf_hash
216
	 */
217
	public function get_csrf_hash()
218
	{
219
		return $this->_csrf_hash;
220
	}
221
222
	// --------------------------------------------------------------------
223
224
	/**
225
	 * Get CSRF Token Name
226
	 *
227
	 * Getter Method
228
	 *
229
	 * @return 	string 	self::csrf_token_name
230
	 */
231
	public function get_csrf_token_name()
232
	{
233
		return $this->_csrf_token_name;
234
	}
235
236
	// --------------------------------------------------------------------
237
238
	/**
239
	 * XSS Clean
240
	 *
241
	 * Sanitizes data so that Cross Site Scripting Hacks can be
242
	 * prevented.  This function does a fair amount of work but
243
	 * it is extremely thorough, designed to prevent even the
244
	 * most obscure XSS attempts.  Nothing is ever 100% foolproof,
245
	 * of course, but I haven't been able to get anything passed
246
	 * the filter.
247
	 *
248
	 * Note: This function should only be used to deal with data
249
	 * upon submission.  It's not something that should
250
	 * be used for general runtime processing.
251
	 *
252
	 * This function was based in part on some code and ideas I
253
	 * got from Bitflux: http://channel.bitflux.ch/wiki/XSS_Prevention
254
	 *
255
	 * To help develop this script I used this great list of
256
	 * vulnerabilities along with a few other hacks I've
257
	 * harvested from examining vulnerabilities in other programs:
258
	 * http://ha.ckers.org/xss.html
259
	 *
260
	 * @param	mixed	string or array
261
	 * @param 	bool
262
	 * @return	string
263
	 */
264
	public function xss_clean($str, $is_image = FALSE)
265
	{
266
		/*
267
		 * Is the string an array?
268
		 *
269
		 */
270
		if (is_array($str))
271
		{
272
			while (list($key) = each($str))
273
			{
274
				$str[$key] = $this->xss_clean($str[$key]);
275
			}
276
277
			return $str;
278
		}
279
280
		/*
281
		 * Remove Invisible Characters
282
		 */
283
		$str = remove_invisible_characters($str);
284
285
		// Validate Entities in URLs
286
		$str = $this->_validate_entities($str);
287
288
		/*
289
		 * URL Decode
290
		 *
291
		 * Just in case stuff like this is submitted:
292
		 *
293
		 * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
294
		 *
295
		 * Note: Use rawurldecode() so it does not remove plus signs
296
		 *
297
		 */
298
		$str = rawurldecode($str);
299
300
		/*
301
		 * Convert character entities to ASCII
302
		 *
303
		 * This permits our tests below to work reliably.
304
		 * We only convert entities that are within tags since
305
		 * these are the ones that will pose security problems.
306
		 *
307
		 */
308
309
		$str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str);
310
311
		$str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str);
312
313
		/*
314
		 * Remove Invisible Characters Again!
315
		 */
316
		$str = remove_invisible_characters($str);
317
318
		/*
319
		 * Convert all tabs to spaces
320
		 *
321
		 * This prevents strings like this: ja	vascript
322
		 * NOTE: we deal with spaces between characters later.
323
		 * NOTE: preg_replace was found to be amazingly slow here on
324
		 * large blocks of data, so we use str_replace.
325
		 */
326
327
		if (strpos($str, "\t") !== FALSE)
328
		{
329
			$str = str_replace("\t", ' ', $str);
330
		}
331
332
		/*
333
		 * Capture converted string for later comparison
334
		 */
335
		$converted_string = $str;
336
337
		// Remove Strings that are never allowed
338
		$str = $this->_do_never_allowed($str);
339
340
		/*
341
		 * Makes PHP tags safe
342
		 *
343
		 * Note: XML tags are inadvertently replaced too:
344
		 *
345
		 * <?xml
346
		 *
347
		 * But it doesn't seem to pose a problem.
348
		 */
349
		if ($is_image === TRUE)
350
		{
351
			// Images have a tendency to have the PHP short opening and
352
			// closing tags every so often so we skip those and only
353
			// do the long opening tags.
354
			$str = preg_replace('/<\?(php)/i', "&lt;?\\1", $str);
355
		}
356
		else
357
		{
358
			$str = str_replace(array('<?', '?'.'>'),  array('&lt;?', '?&gt;'), $str);
359
		}
360
361
		/*
362
		 * Compact any exploded words
363
		 *
364
		 * This corrects words like:  j a v a s c r i p t
365
		 * These words are compacted back to their correct state.
366
		 */
367
		$words = array(
368
			'javascript', 'expression', 'vbscript', 'script', 'base64',
369
			'applet', 'alert', 'document', 'write', 'cookie', 'window'
370
		);
371
372
		foreach ($words as $word)
373
		{
374
			$temp = '';
375
376
			for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++)
377
			{
378
				$temp .= substr($word, $i, 1)."\s*";
379
			}
380
381
			// We only want to do this when it is followed by a non-word character
382
			// That way valid stuff like "dealer to" does not become "dealerto"
383
			$str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str);
384
		}
385
386
		/*
387
		 * Remove disallowed Javascript in links or img tags
388
		 * We used to do some version comparisons and use of stripos for PHP5,
389
		 * but it is dog slow compared to these simplified non-capturing
390
		 * preg_match(), especially if the pattern exists in the string
391
		 */
392
		do
393
		{
394
			$original = $str;
395
396
			if (preg_match("/<a/i", $str))
397
			{
398
				$str = preg_replace_callback("#<a\s+([^>]*?)(>|$)#si", array($this, '_js_link_removal'), $str);
399
			}
400
401
			if (preg_match("/<img/i", $str))
402
			{
403
				$str = preg_replace_callback("#<img\s+([^>]*?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str);
404
			}
405
406
			if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str))
407
			{
408
				$str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str);
409
			}
410
		}
411
		while($original != $str);
412
413
		unset($original);
414
415
		// Remove evil attributes such as style, onclick and xmlns
416
		$str = $this->_remove_evil_attributes($str, $is_image);
417
418
		/*
419
		 * Sanitize naughty HTML elements
420
		 *
421
		 * If a tag containing any of the words in the list
422
		 * below is found, the tag gets converted to entities.
423
		 *
424
		 * So this: <blink>
425
		 * Becomes: &lt;blink&gt;
426
		 */
427
		$naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss';
428
		$str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str);
429
430
		/*
431
		 * Sanitize naughty scripting elements
432
		 *
433
		 * Similar to above, only instead of looking for
434
		 * tags it looks for PHP and JavaScript commands
435
		 * that are disallowed.  Rather than removing the
436
		 * code, it simply converts the parenthesis to entities
437
		 * rendering the code un-executable.
438
		 *
439
		 * For example:	eval('some code')
440
		 * Becomes:		eval&#40;'some code'&#41;
441
		 */
442
		$str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2&#40;\\3&#41;", $str);
443
444
445
		// Final clean up
446
		// This adds a bit of extra precaution in case
447
		// something got through the above filters
448
		$str = $this->_do_never_allowed($str);
449
450
		/*
451
		 * Images are Handled in a Special Way
452
		 * - Essentially, we want to know that after all of the character
453
		 * conversion is done whether any unwanted, likely XSS, code was found.
454
		 * If not, we return TRUE, as the image is clean.
455
		 * However, if the string post-conversion does not matched the
456
		 * string post-removal of XSS, then it fails, as there was unwanted XSS
457
		 * code found and removed/changed during processing.
458
		 */
459
460
		if ($is_image === TRUE)
461
		{
462
			return ($str == $converted_string) ? TRUE: FALSE;
463
		}
464
465
		log_message('debug', "XSS Filtering completed");
466
		return $str;
467
	}
468
469
	// --------------------------------------------------------------------
470
471
	/**
472
	 * Random Hash for protecting URLs
473
	 *
474
	 * @return	string
475
	 */
476
	public function xss_hash()
477
	{
478
		if ($this->_xss_hash == '')
479
		{
480
			mt_srand();
481
			$this->_xss_hash = md5(time() + mt_rand(0, 1999999999));
482
		}
483
484
		return $this->_xss_hash;
485
	}
486
487
	// --------------------------------------------------------------------
488
489
	/**
490
	 * HTML Entities Decode
491
	 *
492
	 * This function is a replacement for html_entity_decode()
493
	 *
494
	 * The reason we are not using html_entity_decode() by itself is because
495
	 * while it is not technically correct to leave out the semicolon
496
	 * at the end of an entity most browsers will still interpret the entity
497
	 * correctly.  html_entity_decode() does not convert entities without
498
	 * semicolons, so we are left with our own little solution here. Bummer.
499
	 *
500
	 * @param	string
501
	 * @param	string
502
	 * @return	string
503
	 */
504
	public function entity_decode($str, $charset='UTF-8')
505
	{
506
		if (stristr($str, '&') === FALSE)
507
		{
508
			return $str;
509
		}
510
511
		$str = html_entity_decode($str, ENT_COMPAT, $charset);
512
		$str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str);
513
		return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str);
514
	}
515
516
	// --------------------------------------------------------------------
517
518
	/**
519
	 * Filename Security
520
	 *
521
	 * @param	string
522
	 * @param 	bool
523
	 * @return	string
524
	 */
525
	public function sanitize_filename($str, $relative_path = FALSE)
526
	{
527
		$bad = array(
528
			"../",
529
			"<!--",
530
			"-->",
531
			"<",
532
			">",
533
			"'",
534
			'"',
535
			'&',
536
			'$',
537
			'#',
538
			'{',
539
			'}',
540
			'[',
541
			']',
542
			'=',
543
			';',
544
			'?',
545
			"%20",
546
			"%22",
547
			"%3c",		// <
548
			"%253c",	// <
549
			"%3e",		// >
550
			"%0e",		// >
551
			"%28",		// (
552
			"%29",		// )
553
			"%2528",	// (
554
			"%26",		// &
555
			"%24",		// $
556
			"%3f",		// ?
557
			"%3b",		// ;
558
			"%3d"		// =
559
		);
560
561
		if ( ! $relative_path)
562
		{
563
			$bad[] = './';
564
			$bad[] = '/';
565
		}
566
567
		$str = remove_invisible_characters($str, FALSE);
568
		return stripslashes(str_replace($bad, '', $str));
569
	}
570
571
	// ----------------------------------------------------------------
572
573
	/**
574
	 * Compact Exploded Words
575
	 *
576
	 * Callback function for xss_clean() to remove whitespace from
577
	 * things like j a v a s c r i p t
578
	 *
579
	 * @param	type
580
	 * @return	type
581
	 */
582
	protected function _compact_exploded_words($matches)
583
	{
584
		return preg_replace('/\s+/s', '', $matches[1]).$matches[2];
585
	}
586
587
	// --------------------------------------------------------------------
588
589
	/*
590
	 * Remove Evil HTML Attributes (like evenhandlers and style)
591
	 *
592
	 * It removes the evil attribute and either:
593
	 * 	- Everything up until a space
594
	 *		For example, everything between the pipes:
595
	 *		<a |style=document.write('hello');alert('world');| class=link>
596
	 * 	- Everything inside the quotes
597
	 *		For example, everything between the pipes:
598
	 *		<a |style="document.write('hello'); alert('world');"| class="link">
599
	 *
600
	 * @param string $str The string to check
601
	 * @param boolean $is_image TRUE if this is an image
602
	 * @return string The string with the evil attributes removed
603
	 */
604
	protected function _remove_evil_attributes($str, $is_image)
605
	{
606
		// All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns
607
		$evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction');
608
609
		if ($is_image === TRUE)
610
		{
611
			/*
612
			 * Adobe Photoshop puts XML metadata into JFIF images, 
613
			 * including namespacing, so we have to allow this for images.
614
			 */
615
			unset($evil_attributes[array_search('xmlns', $evil_attributes)]);
616
		}
617
618
		do {
619
			$count = 0;
620
			$attribs = array();
621
622
			// find occurrences of illegal attribute strings without quotes
623
			preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER);
624
625
			foreach ($matches as $attr)
626
			{
627
628
				$attribs[] = preg_quote($attr[0], '/');
629
			}
630
631
			// find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes)
632
			preg_match_all("/(".implode('|', $evil_attributes).")\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is",  $str, $matches, PREG_SET_ORDER);
633
634
			foreach ($matches as $attr)
635
			{
636
				$attribs[] = preg_quote($attr[0], '/');
637
			}
638
639
			// replace illegal attribute strings that are inside an html tag
640
			if (count($attribs) > 0)
641
			{
642
				$str = preg_replace("/<(\/?[^><]+?)([^A-Za-z<>\-])(.*?)(".implode('|', $attribs).")(.*?)([\s><])([><]*)/i", '<$1 $3$5$6$7', $str, -1, $count);
643
			}
644
645
		} while ($count);
646
647
		return $str;
648
	}
649
650
	// --------------------------------------------------------------------
651
652
	/**
653
	 * Sanitize Naughty HTML
654
	 *
655
	 * Callback function for xss_clean() to remove naughty HTML elements
656
	 *
657
	 * @param	array
658
	 * @return	string
659
	 */
660
	protected function _sanitize_naughty_html($matches)
661
	{
662
		// encode opening brace
663
		$str = '&lt;'.$matches[1].$matches[2].$matches[3];
664
665
		// encode captured opening or closing brace to prevent recursive vectors
666
		$str .= str_replace(array('>', '<'), array('&gt;', '&lt;'),
667
							$matches[4]);
668
669
		return $str;
670
	}
671
672
	// --------------------------------------------------------------------
673
674
	/**
675
	 * JS Link Removal
676
	 *
677
	 * Callback function for xss_clean() to sanitize links
678
	 * This limits the PCRE backtracks, making it more performance friendly
679
	 * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
680
	 * PHP 5.2+ on link-heavy strings
681
	 *
682
	 * @param	array
683
	 * @return	string
684
	 */
685
	protected function _js_link_removal($match)
686
	{
687
		return str_replace(
688
			$match[1],
689
			preg_replace(
690
				'#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
691
				'',
692
				$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
693
			),
694
			$match[0]
695
		);
696
	}
697
698
	// --------------------------------------------------------------------
699
700
	/**
701
	 * JS Image Removal
702
	 *
703
	 * Callback function for xss_clean() to sanitize image tags
704
	 * This limits the PCRE backtracks, making it more performance friendly
705
	 * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
706
	 * PHP 5.2+ on image tag heavy strings
707
	 *
708
	 * @param	array
709
	 * @return	string
710
	 */
711
	protected function _js_img_removal($match)
712
	{
713
		return str_replace(
714
			$match[1],
715
			preg_replace(
716
				'#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
717
				'',
718
				$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
719
			),
720
			$match[0]
721
		);
722
	}
723
724
	// --------------------------------------------------------------------
725
726
	/**
727
	 * Attribute Conversion
728
	 *
729
	 * Used as a callback for XSS Clean
730
	 *
731
	 * @param	array
732
	 * @return	string
733
	 */
734
	protected function _convert_attribute($match)
735
	{
736
		return str_replace(array('>', '<', '\\'), array('&gt;', '&lt;', '\\\\'), $match[0]);
737
	}
738
739
	// --------------------------------------------------------------------
740
741
	/**
742
	 * Filter Attributes
743
	 *
744
	 * Filters tag attributes for consistency and safety
745
	 *
746
	 * @param	string
747
	 * @return	string
748
	 */
749
	protected function _filter_attributes($str)
750
	{
751
		$out = '';
752
753
		if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches))
754
		{
755
			foreach ($matches[0] as $match)
756
			{
757
				$out .= preg_replace("#/\*.*?\*/#s", '', $match);
758
			}
759
		}
760
761
		return $out;
762
	}
763
764
	// --------------------------------------------------------------------
765
766
	/**
767
	 * HTML Entity Decode Callback
768
	 *
769
	 * Used as a callback for XSS Clean
770
	 *
771
	 * @param	array
772
	 * @return	string
773
	 */
774
	protected function _decode_entity($match)
775
	{
776
		return $this->entity_decode($match[0], strtoupper(config_item('charset')));
777
	}
778
779
	// --------------------------------------------------------------------
780
781
	/**
782
	 * Validate URL entities
783
	 *
784
	 * Called by xss_clean()
785
	 *
786
	 * @param 	string
787
	 * @return 	string
788
	 */
789
	protected function _validate_entities($str)
790
	{
791
		/*
792
		 * Protect GET variables in URLs
793
		 */
794
795
		 // 901119URL5918AMP18930PROTECT8198
796
797
		$str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash()."\\1=\\2", $str);
798
799
		/*
800
		 * Validate standard character entities
801
		 *
802
		 * Add a semicolon if missing.  We do this to enable
803
		 * the conversion of entities to ASCII later.
804
		 *
805
		 */
806
		$str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str);
807
808
		/*
809
		 * Validate UTF16 two byte encoding (x00)
810
		 *
811
		 * Just as above, adds a semicolon if missing.
812
		 *
813
		 */
814
		$str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str);
815
816
		/*
817
		 * Un-Protect GET variables in URLs
818
		 */
819
		$str = str_replace($this->xss_hash(), '&', $str);
820
821
		return $str;
822
	}
823
824
	// ----------------------------------------------------------------------
825
826
	/**
827
	 * Do Never Allowed
828
	 *
829
	 * A utility function for xss_clean()
830
	 *
831
	 * @param 	string
832
	 * @return 	string
833
	 */
834
	protected function _do_never_allowed($str)
835
	{
836
		$str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);
837
838
		foreach ($this->_never_allowed_regex as $regex)
839
		{
840
			$str = preg_replace('#'.$regex.'#is', '[removed]', $str);
841
		}
842
843
		return $str;
844
	}
845
846
	// --------------------------------------------------------------------
847
848
	/**
849
	 * Set Cross Site Request Forgery Protection Cookie
850
	 *
851
	 * @return	string
852
	 */
853
	protected function _csrf_set_hash()
854
	{
855
		if ($this->_csrf_hash == '')
856
		{
857
			// If the cookie exists we will use it's value.
858
			// We don't necessarily want to regenerate it with
859
			// each page load since a page could contain embedded
860
			// sub-pages causing this feature to fail
861
			if (isset($_COOKIE[$this->_csrf_cookie_name]) &&
862
				preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)
863
			{
864
				return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];
865
			}
866
867
			return $this->_csrf_hash = md5(uniqid(rand(), TRUE));
868
		}
869
870
		return $this->_csrf_hash;
871
	}
872
873
}
874
875
/* End of file Security.php */
876
/* Location: ./system/libraries/Security.php */