Completed
Push — master ( c8c0f9...82094b )
by Maxime
19:00
created

Security::_remove_evil_attributes()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 32
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 32
ccs 0
cts 24
cp 0
rs 8.5806
cc 4
eloc 14
nc 4
nop 3
crap 20
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: mfrancois
5
 * Date: 04/07/2016
6
 * Time: 11:32
7
 */
8
9
namespace Distilleries\Expendable\Helpers;
10
11
12
class Security
13
{
14
15
16
    /**
17
     * Random Hash for protecting URLs
18
     *
19
     * @var string
20
     * @access protected
21
     */
22
    protected $_xss_hash			= '';
23
24
    /**
25
     * List of never allowed strings
26
     *
27
     * @var array
28
     * @access protected
29
     */
30
    protected $_never_allowed_str = array(
31
        'document.cookie'	=> '[removed]',
32
        'document.write'	=> '[removed]',
33
        '.parentNode'		=> '[removed]',
34
        '.innerHTML'		=> '[removed]',
35
        'window.location'	=> '[removed]',
36
        '-moz-binding'		=> '[removed]',
37
        '<!--'				=> '&lt;!--',
38
        '-->'				=> '--&gt;',
39
        '<![CDATA['			=> '&lt;![CDATA[',
40
        '<comment>'			=> '&lt;comment&gt;'
41
    );
42
43
    /* never allowed, regex replacement */
44
    /**
45
     * List of never allowed regex replacement
46
     *
47
     * @var array
48
     * @access protected
49
     */
50
    protected $_never_allowed_regex = array(
51
        'javascript\s*:',
52
        'expression\s*(\(|&\#40;)', // CSS and IE
53
        'vbscript\s*:', // IE, surprise!
54
        'Redirect\s+302',
55
        "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?"
56
    );
57
58
59
    /**
60
     * XSS Clean
61
     *
62
     * Sanitizes data so that Cross Site Scripting Hacks can be
63
     * prevented.  This function does a fair amount of work but
64
     * it is extremely thorough, designed to prevent even the
65
     * most obscure XSS attempts.  Nothing is ever 100% foolproof,
66
     * of course, but I haven't been able to get anything passed
67
     * the filter.
68
     *
69
     * Note: This function should only be used to deal with data
70
     * upon submission.  It's not something that should
71
     * be used for general runtime processing.
72
     *
73
     * This function was based in part on some code and ideas I
74
     * got from Bitflux: http://channel.bitflux.ch/wiki/XSS_Prevention
75
     *
76
     * To help develop this script I used this great list of
77
     * vulnerabilities along with a few other hacks I've
78
     * harvested from examining vulnerabilities in other programs:
79
     * http://ha.ckers.org/xss.html
80
     *
81
     * @param    mixed    string or array
82
     * @return    string
83
     */
84
    public function xss_clean($str, $is_image = FALSE, $evilAttribute = TRUE)
85
    {
86
        /*
87
           * Is the string an array?
88
           *
89
           */
90
        if (is_array($str))
91
        {
92
            while (list($key) = each($str))
93
            {
94
                $str[$key] = $this->xss_clean($str[$key], $is_image, $evilAttribute);
95
            }
96
            return $str;
97
        }
98
99
        /*
100
           * Remove Invisible Characters
101
           */
102
        $str = $this->remove_invisible_characters($str);
103
104
        // Validate Entities in URLs
105
        $str = $this->_validate_entities($str);
106
107
        /*
108
           * URL Decode
109
           *
110
           * Just in case stuff like this is submitted:
111
           *
112
           * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
113
           *
114
           * Note: Use rawurldecode() so it does not remove plus signs
115
           *
116
           */
117
        $str = rawurldecode($str);
118
119
        /*
120
           * Convert character entities to ASCII
121
           *
122
           * This permits our tests below to work reliably.
123
           * We only convert entities that are within tags since
124
           * these are the ones that will pose security problems.
125
           *
126
           */
127
128
        $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str);
129
130
        $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str);
131
132
        /*
133
           * Remove Invisible Characters Again!
134
           */
135
        $str = $this->remove_invisible_characters($str);
136
137
        /*
138
           * Convert all tabs to spaces
139
           *
140
           * This prevents strings like this: ja	vascript
141
           * NOTE: we deal with spaces between characters later.
142
           * NOTE: preg_replace was found to be amazingly slow here on
143
           * large blocks of data, so we use str_replace.
144
           */
145
146
        if (strpos($str, "\t") !== FALSE)
147
        {
148
            $str = str_replace("\t", ' ', $str);
149
        }
150
151
        /*
152
           * Capture converted string for later comparison
153
           */
154
        $converted_string = $str;
155
156
        // Remove Strings that are never allowed
157
        $str = $this->_do_never_allowed($str);
158
159
        /*
160
           * Makes PHP tags safe
161
           *
162
           * Note: XML tags are inadvertently replaced too:
163
           *
164
           * <?xml
165
           *
166
           * But it doesn't seem to pose a problem.
167
           */
168
        if ($is_image === TRUE)
169
        {
170
            // Images have a tendency to have the PHP short opening and
171
            // closing tags every so often so we skip those and only
172
            // do the long opening tags.
173
            $str = preg_replace('/<\?(php)/i', "&lt;?\\1", $str);
174
        }
175
        else
176
        {
177
            $str = str_replace(array('<?', '?' . '>'), array('&lt;?', '?&gt;'), $str);
178
        }
179
180
        /*
181
           * Compact any exploded words
182
           *
183
           * This corrects words like:  j a v a s c r i p t
184
           * These words are compacted back to their correct state.
185
           */
186
        $words = array(
187
            'javascript', 'expression', 'vbscript', 'script',
188
            'applet', 'alert', 'document', 'write', 'cookie', 'window'
189
        );
190
191
        foreach ($words as $word)
192
        {
193
            $temp = '';
194
195
            for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++)
196
            {
197
                $temp .= substr($word, $i, 1) . "\s*";
198
            }
199
200
            // We only want to do this when it is followed by a non-word character
201
            // That way valid stuff like "dealer to" does not become "dealerto"
202
            $str = preg_replace_callback('#(' . substr($temp, 0, -3) . ')(\W)#is', array($this, '_compact_exploded_words'), $str);
203
        }
204
205
        /*
206
           * Remove disallowed Javascript in links or img tags
207
           * We used to do some version comparisons and use of stripos for PHP5,
208
           * but it is dog slow compared to these simplified non-capturing
209
           * preg_match(), especially if the pattern exists in the string
210
           */
211
        do
212
        {
213
            $original = $str;
214
215
            if (preg_match("/<a/i", $str))
216
            {
217
                $str = preg_replace_callback("#<a\s+([^>]*?)(>|$)#si", array($this, '_js_link_removal'), $str);
218
            }
219
220
            if (preg_match("/<img/i", $str))
221
            {
222
                $str = preg_replace_callback("#<img\s+([^>]*?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str);
223
            }
224
225
            if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str))
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
226
            {
227
                $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str);
228
            }
229
        }
230
        while ($original != $str);
231
232
        unset($original);
233
234
        // Remove evil attributes such as style, onclick and xmlns
235
        $str = $this->_remove_evil_attributes($str, $is_image, $evilAttribute);
236
237
        /*
238
           * Sanitize naughty HTML elements
239
           *
240
           * If a tag containing any of the words in the list
241
           * below is found, the tag gets converted to entities.
242
           *
243
           * So this: <blink>
244
           * Becomes: &lt;blink&gt;
245
           */
246
        $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';
247
        $str     = preg_replace_callback('#<(/*\s*)(' . $naughty . ')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str);
248
249
        /*
250
           * Sanitize naughty scripting elements
251
           *
252
           * Similar to above, only instead of looking for
253
           * tags it looks for PHP and JavaScript commands
254
           * that are disallowed.  Rather than removing the
255
           * code, it simply converts the parenthesis to entities
256
           * rendering the code un-executable.
257
           *
258
           * For example:	eval('some code')
259
           * Becomes:		eval&#40;'some code'&#41;
260
           */
261
        $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);
262
263
264
        // Final clean up
265
        // This adds a bit of extra precaution in case
266
        // something got through the above filters
267
        $str = $this->_do_never_allowed($str);
268
269
        /*
270
           * Images are Handled in a Special Way
271
           * - Essentially, we want to know that after all of the character
272
           * conversion is done whether any unwanted, likely XSS, code was found.
273
           * If not, we return TRUE, as the image is clean.
274
           * However, if the string post-conversion does not matched the
275
           * string post-removal of XSS, then it fails, as there was unwanted XSS
276
           * code found and removed/changed during processing.
277
           */
278
279
        if ($is_image === TRUE)
280
        {
281
            return ($str == $converted_string) ? TRUE : FALSE;
282
        }
283
284
        return $str;
285
    }
286
287
    // --------------------------------------------------------------------
288
289
    /*
290
      * Remove Evil HTML Attributes (like evenhandlers and style)
291
      *
292
      * It removes the evil attribute and either:
293
      * 	- Everything up until a space
294
      *		For example, everything between the pipes:
295
      *		<a |style=document.write('hello');alert('world');| class=link>
296
      * 	- Everything inside the quotes
297
      *		For example, everything between the pipes:
298
      *		<a |style="document.write('hello'); alert('world');"| class="link">
299
      *
300
      * @param string $str The string to check
301
      * @param boolean $is_image TRUE if this is an image
302
      * @return string The string with the evil attributes removed
303
      */
304
    protected function _remove_evil_attributes($str, $is_image, $evilAttribute)
305
    {
306
        // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns
307
        if ($evilAttribute)
308
        {
309
            $evil_attributes = array('on\w*', 'style', 'xmlns');
310
        }
311
        else
312
        {
313
            $evil_attributes = array('on\w*', 'xmlns');
314
        }
315
316
        if ($is_image === TRUE)
317
        {
318
            /*
319
                * Adobe Photoshop puts XML metadata into JFIF images,
320
                * including namespacing, so we have to allow this for images.
321
                */
322
            unset($evil_attributes[array_search('xmlns', $evil_attributes)]);
323
        }
324
325
        do
326
        {
327
            $str = preg_replace(
328
                "#<(/?[^><]+?)([^A-Za-z\-])(" . implode('|', $evil_attributes) . ")(\s*=\s*)([\"][^>]*?[\"]|[\'][^>]*?[\']|[^>]*?)([\s><])([><]*)#i",
329
                "<$1$6",
330
                $str, -1, $count
331
            );
332
        } while ($count);
0 ignored issues
show
Bug Best Practice introduced by
The expression $count of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
333
334
        return $str;
335
    }
336
337
338
339
    // --------------------------------------------------------------------
340
341
    /**
342
     * HTML Entities Decode
343
     *
344
     * This function is a replacement for html_entity_decode()
345
     *
346
     * The reason we are not using html_entity_decode() by itself is because
347
     * while it is not technically correct to leave out the semicolon
348
     * at the end of an entity most browsers will still interpret the entity
349
     * correctly.  html_entity_decode() does not convert entities without
350
     * semicolons, so we are left with our own little solution here. Bummer.
351
     *
352
     * @param	string
353
     * @param	string
354
     * @return	string
355
     */
356
    public function entity_decode($str, $charset='UTF-8')
357
    {
358
        if (stristr($str, '&') === FALSE)
359
        {
360
            return $str;
361
        }
362
363
        $str = html_entity_decode($str, ENT_COMPAT, $charset);
364
        $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str);
365
        return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str);
366
    }
367
368
    // --------------------------------------------------------------------
369
370
    /**
371
     * Filename Security
372
     *
373
     * @param	string
374
     * @param 	bool
375
     * @return	string
376
     */
377
    public function sanitize_filename($str, $relative_path = FALSE)
378
    {
379
        $bad = array(
380
            "../",
381
            "<!--",
382
            "-->",
383
            "<",
384
            ">",
385
            "'",
386
            '"',
387
            '&',
388
            '$',
389
            '#',
390
            '{',
391
            '}',
392
            '[',
393
            ']',
394
            '=',
395
            ';',
396
            '?',
397
            "%20",
398
            "%22",
399
            "%3c",		// <
400
            "%253c",	// <
401
            "%3e",		// >
402
            "%0e",		// >
403
            "%28",		// (
404
            "%29",		// )
405
            "%2528",	// (
406
            "%26",		// &
407
            "%24",		// $
408
            "%3f",		// ?
409
            "%3b",		// ;
410
            "%3d"		// =
411
        );
412
413
        if ( ! $relative_path)
414
        {
415
            $bad[] = './';
416
            $bad[] = '/';
417
        }
418
419
        $str = $this->remove_invisible_characters($str, FALSE);
420
        return stripslashes(str_replace($bad, '', $str));
421
    }
422
423
    // ----------------------------------------------------------------
424
425
    /**
426
     * Compact Exploded Words
427
     *
428
     * Callback function for xss_clean() to remove whitespace from
429
     * things like j a v a s c r i p t
430
     *
431
     * @param	type
432
     * @return	type
433
     */
434
    protected function _compact_exploded_words($matches)
435
    {
436
        return preg_replace('/\s+/s', '', $matches[1]).$matches[2];
437
    }
438
439
    // --------------------------------------------------------------------
440
441
    /**
442
     * Sanitize Naughty HTML
443
     *
444
     * Callback function for xss_clean() to remove naughty HTML elements
445
     *
446
     * @param	array
447
     * @return	string
448
     */
449
    protected function _sanitize_naughty_html($matches)
450
    {
451
        // encode opening brace
452
        $str = '&lt;'.$matches[1].$matches[2].$matches[3];
453
454
        // encode captured opening or closing brace to prevent recursive vectors
455
        $str .= str_replace(array('>', '<'), array('&gt;', '&lt;'),
456
            $matches[4]);
457
458
        return $str;
459
    }
460
461
    // --------------------------------------------------------------------
462
463
    /**
464
     * JS Link Removal
465
     *
466
     * Callback function for xss_clean() to sanitize links
467
     * This limits the PCRE backtracks, making it more performance friendly
468
     * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
469
     * PHP 5.2+ on link-heavy strings
470
     *
471
     * @param	array
472
     * @return	string
473
     */
474 View Code Duplication
    protected function _js_link_removal($match)
475
    {
476
        return str_replace(
477
            $match[1],
478
            preg_replace(
479
                '#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
480
                '',
481
                $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
482
            ),
483
            $match[0]
484
        );
485
    }
486
487
    // --------------------------------------------------------------------
488
489
    /**
490
     * JS Image Removal
491
     *
492
     * Callback function for xss_clean() to sanitize image tags
493
     * This limits the PCRE backtracks, making it more performance friendly
494
     * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
495
     * PHP 5.2+ on image tag heavy strings
496
     *
497
     * @param	array
498
     * @return	string
499
     */
500 View Code Duplication
    protected function _js_img_removal($match)
501
    {
502
        return str_replace(
503
            $match[1],
504
            preg_replace(
505
                '#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
506
                '',
507
                $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
508
            ),
509
            $match[0]
510
        );
511
    }
512
513
    // --------------------------------------------------------------------
514
515
    /**
516
     * Attribute Conversion
517
     *
518
     * Used as a callback for XSS Clean
519
     *
520
     * @param	array
521
     * @return	string
522
     */
523
    protected function _convert_attribute($match)
524
    {
525
        return str_replace(array('>', '<', '\\'), array('&gt;', '&lt;', '\\\\'), $match[0]);
526
    }
527
528
    // --------------------------------------------------------------------
529
530
    /**
531
     * Filter Attributes
532
     *
533
     * Filters tag attributes for consistency and safety
534
     *
535
     * @param	string
536
     * @return	string
537
     */
538
    protected function _filter_attributes($str)
539
    {
540
        $out = '';
541
542
        if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches))
543
        {
544
            foreach ($matches[0] as $match)
545
            {
546
                $out .= preg_replace("#/\*.*?\*/#s", '', $match);
547
            }
548
        }
549
550
        return $out;
551
    }
552
553
    // --------------------------------------------------------------------
554
555
    /**
556
     * HTML Entity Decode Callback
557
     *
558
     * Used as a callback for XSS Clean
559
     *
560
     * @param	array
561
     * @return	string
562
     */
563
    protected function _decode_entity($match)
564
    {
565
        return $this->entity_decode($match[0], strtoupper(config_item('charset')));
566
    }
567
568
    // --------------------------------------------------------------------
569
570
    /**
571
     * Validate URL entities
572
     *
573
     * Called by xss_clean()
574
     *
575
     * @param 	string
576
     * @return 	string
577
     */
578
    protected function _validate_entities($str)
579
    {
580
        /*
581
         * Protect GET variables in URLs
582
         */
583
584
        // 901119URL5918AMP18930PROTECT8198
585
586
        $str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash()."\\1=\\2", $str);
587
588
        /*
589
         * Validate standard character entities
590
         *
591
         * Add a semicolon if missing.  We do this to enable
592
         * the conversion of entities to ASCII later.
593
         *
594
         */
595
        $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str);
596
597
        /*
598
         * Validate UTF16 two byte encoding (x00)
599
         *
600
         * Just as above, adds a semicolon if missing.
601
         *
602
         */
603
        $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str);
604
605
606
        return $str;
607
    }
608
609
    // ----------------------------------------------------------------------
610
611
    /**
612
     * Do Never Allowed
613
     *
614
     * A utility function for xss_clean()
615
     *
616
     * @param 	string
617
     * @return 	string
618
     */
619
    protected function _do_never_allowed($str)
620
    {
621
        $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);
622
623
        foreach ($this->_never_allowed_regex as $regex)
624
        {
625
            $str = preg_replace('#'.$regex.'#is', '[removed]', $str);
626
        }
627
628
        return $str;
629
    }
630
631
632
    protected function remove_invisible_characters($str, $url_encoded = TRUE)
633
    {
634
        $non_displayables = array();
635
636
        // every control character except newline (dec 10)
637
        // carriage return (dec 13), and horizontal tab (dec 09)
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
638
639
        if ($url_encoded)
640
        {
641
            $non_displayables[] = '/%0[0-8bcef]/';	// url encoded 00-08, 11, 12, 14, 15
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
642
            $non_displayables[] = '/%1[0-9a-f]/';	// url encoded 16-31
643
        }
644
645
        $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';	// 00-08, 11, 12, 14-31, 127
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
646
647
        do
648
        {
649
            $str = preg_replace($non_displayables, '', $str, -1, $count);
650
        }
651
        while ($count);
0 ignored issues
show
Bug Best Practice introduced by
The expression $count of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
652
653
        return $str;
654
    }
655
656
    /**
657
     * Random Hash for protecting URLs
658
     *
659
     * @return	string
660
     */
661
    public function xss_hash()
662
    {
663
        if ($this->_xss_hash == '')
664
        {
665
            mt_srand();
666
            $this->_xss_hash = md5(time() + mt_rand(0, 1999999999));
667
        }
668
669
        return $this->_xss_hash;
670
    }
671
672
673
}