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