Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
10582 lgm 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 with quotes (042 and 047 are octal quotes)
623
			preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is', $str, $matches, PREG_SET_ORDER);
624
 
625
			foreach ($matches as $attr)
626
			{
627
				$attribs[] = preg_quote($attr[0], '/');
628
			}
629
 
630
			// find occurrences of illegal attribute strings without quotes
631
			preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER);
632
 
633
			foreach ($matches as $attr)
634
			{
635
				$attribs[] = preg_quote($attr[0], '/');
636
			}
637
 
638
			// replace illegal attribute strings that are inside an html tag
639
			if (count($attribs) > 0)
640
			{
641
				$str = preg_replace('/(<?)(\/?[^><]+?)([^A-Za-z<>\-])(.*?)('.implode('|', $attribs).')(.*?)([\s><]?)([><]*)/i', '$1$2 $4$6$7$8', $str, -1, $count);
642
			}
643
 
644
		} while ($count);
645
 
646
		return $str;
647
	}
648
 
649
	// --------------------------------------------------------------------
650
 
651
	/**
652
	 * Sanitize Naughty HTML
653
	 *
654
	 * Callback function for xss_clean() to remove naughty HTML elements
655
	 *
656
	 * @param	array
657
	 * @return	string
658
	 */
659
	protected function _sanitize_naughty_html($matches)
660
	{
661
		// encode opening brace
662
		$str = '&lt;'.$matches[1].$matches[2].$matches[3];
663
 
664
		// encode captured opening or closing brace to prevent recursive vectors
665
		$str .= str_replace(array('>', '<'), array('&gt;', '&lt;'),
666
							$matches[4]);
667
 
668
		return $str;
669
	}
670
 
671
	// --------------------------------------------------------------------
672
 
673
	/**
674
	 * JS Link Removal
675
	 *
676
	 * Callback function for xss_clean() to sanitize links
677
	 * This limits the PCRE backtracks, making it more performance friendly
678
	 * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
679
	 * PHP 5.2+ on link-heavy strings
680
	 *
681
	 * @param	array
682
	 * @return	string
683
	 */
684
	protected function _js_link_removal($match)
685
	{
686
		return str_replace(
687
			$match[1],
688
			preg_replace(
689
				'#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
690
				'',
691
				$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
692
			),
693
			$match[0]
694
		);
695
	}
696
 
697
	// --------------------------------------------------------------------
698
 
699
	/**
700
	 * JS Image Removal
701
	 *
702
	 * Callback function for xss_clean() to sanitize image tags
703
	 * This limits the PCRE backtracks, making it more performance friendly
704
	 * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
705
	 * PHP 5.2+ on image tag heavy strings
706
	 *
707
	 * @param	array
708
	 * @return	string
709
	 */
710
	protected function _js_img_removal($match)
711
	{
712
		return str_replace(
713
			$match[1],
714
			preg_replace(
715
				'#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
716
				'',
717
				$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
718
			),
719
			$match[0]
720
		);
721
	}
722
 
723
	// --------------------------------------------------------------------
724
 
725
	/**
726
	 * Attribute Conversion
727
	 *
728
	 * Used as a callback for XSS Clean
729
	 *
730
	 * @param	array
731
	 * @return	string
732
	 */
733
	protected function _convert_attribute($match)
734
	{
735
		return str_replace(array('>', '<', '\\'), array('&gt;', '&lt;', '\\\\'), $match[0]);
736
	}
737
 
738
	// --------------------------------------------------------------------
739
 
740
	/**
741
	 * Filter Attributes
742
	 *
743
	 * Filters tag attributes for consistency and safety
744
	 *
745
	 * @param	string
746
	 * @return	string
747
	 */
748
	protected function _filter_attributes($str)
749
	{
750
		$out = '';
751
 
752
		if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches))
753
		{
754
			foreach ($matches[0] as $match)
755
			{
756
				$out .= preg_replace("#/\*.*?\*/#s", '', $match);
757
			}
758
		}
759
 
760
		return $out;
761
	}
762
 
763
	// --------------------------------------------------------------------
764
 
765
	/**
766
	 * HTML Entity Decode Callback
767
	 *
768
	 * Used as a callback for XSS Clean
769
	 *
770
	 * @param	array
771
	 * @return	string
772
	 */
773
	protected function _decode_entity($match)
774
	{
775
		return $this->entity_decode($match[0], strtoupper(config_item('charset')));
776
	}
777
 
778
	// --------------------------------------------------------------------
779
 
780
	/**
781
	 * Validate URL entities
782
	 *
783
	 * Called by xss_clean()
784
	 *
785
	 * @param 	string
786
	 * @return 	string
787
	 */
788
	protected function _validate_entities($str)
789
	{
790
		/*
791
		 * Protect GET variables in URLs
792
		 */
793
 
794
		 // 901119URL5918AMP18930PROTECT8198
795
 
796
		$str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash()."\\1=\\2", $str);
797
 
798
		/*
799
		 * Validate standard character entities
800
		 *
801
		 * Add a semicolon if missing.  We do this to enable
802
		 * the conversion of entities to ASCII later.
803
		 *
804
		 */
805
		$str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str);
806
 
807
		/*
808
		 * Validate UTF16 two byte encoding (x00)
809
		 *
810
		 * Just as above, adds a semicolon if missing.
811
		 *
812
		 */
813
		$str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str);
814
 
815
		/*
816
		 * Un-Protect GET variables in URLs
817
		 */
818
		$str = str_replace($this->xss_hash(), '&', $str);
819
 
820
		return $str;
821
	}
822
 
823
	// ----------------------------------------------------------------------
824
 
825
	/**
826
	 * Do Never Allowed
827
	 *
828
	 * A utility function for xss_clean()
829
	 *
830
	 * @param 	string
831
	 * @return 	string
832
	 */
833
	protected function _do_never_allowed($str)
834
	{
835
		$str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);
836
 
837
		foreach ($this->_never_allowed_regex as $regex)
838
		{
839
			$str = preg_replace('#'.$regex.'#is', '[removed]', $str);
840
		}
841
 
842
		return $str;
843
	}
844
 
845
	// --------------------------------------------------------------------
846
 
847
	/**
848
	 * Set Cross Site Request Forgery Protection Cookie
849
	 *
850
	 * @return	string
851
	 */
852
	protected function _csrf_set_hash()
853
	{
854
		if ($this->_csrf_hash == '')
855
		{
856
			// If the cookie exists we will use it's value.
857
			// We don't necessarily want to regenerate it with
858
			// each page load since a page could contain embedded
859
			// sub-pages causing this feature to fail
860
			if (isset($_COOKIE[$this->_csrf_cookie_name]) &&
861
				preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)
862
			{
863
				return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];
864
			}
865
 
866
			return $this->_csrf_hash = md5(uniqid(rand(), TRUE));
867
		}
868
 
869
		return $this->_csrf_hash;
870
	}
871
 
872
}
873
 
874
/* End of file Security.php */
875
/* Location: ./system/libraries/Security.php */