Completed
Push — master ( c01fe5...ae1877 )
by Maxime
118:07 queued 115:51
created

Security::escapeLike()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1.037

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 4
cts 6
cp 0.6667
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
crap 1.037
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 = [
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 = [
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
            while (list($key) = each($str)) {
92
                $str[$key] = $this->xss_clean($str[$key], $is_image, $evilAttribute);
93
            }
94
95
            return $str;
96
        }
97
98
        /*
99
           * Remove Invisible Characters
100
           */
101
        $str = $this->remove_invisible_characters($str);
102
103
        // Validate Entities in URLs
104
        $str = $this->_validate_entities($str);
105
106
        /*
107
           * URL Decode
108
           *
109
           * Just in case stuff like this is submitted:
110
           *
111
           * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
112
           *
113
           * Note: Use rawurldecode() so it does not remove plus signs
114
           *
115
           */
116
        $str = rawurldecode($str);
117
118
        /*
119
           * Convert character entities to ASCII
120
           *
121
           * This permits our tests below to work reliably.
122
           * We only convert entities that are within tags since
123
           * these are the ones that will pose security problems.
124
           *
125
           */
126
127
        $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", [$this, '_convert_attribute'], $str);
128
129
        $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", [$this, '_decode_entity'], $str);
130
131
        /*
132
           * Remove Invisible Characters Again!
133
           */
134
        $str = $this->remove_invisible_characters($str);
135
136
        /*
137
           * Convert all tabs to spaces
138
           *
139
           * This prevents strings like this: ja	vascript
140
           * NOTE: we deal with spaces between characters later.
141
           * NOTE: preg_replace was found to be amazingly slow here on
142
           * large blocks of data, so we use str_replace.
143
           */
144
145
        if (strpos($str, "\t") !== false) {
146
            $str = str_replace("\t", ' ', $str);
147
        }
148
149
        /*
150
           * Capture converted string for later comparison
151
           */
152
        $converted_string = $str;
153
154
        // Remove Strings that are never allowed
155
        $str = $this->_do_never_allowed($str);
156
157
        /*
158
           * Makes PHP tags safe
159
           *
160
           * Note: XML tags are inadvertently replaced too:
161
           *
162
           * <?xml
163
           *
164
           * But it doesn't seem to pose a problem.
165
           */
166
        if ($is_image === true) {
167
            // Images have a tendency to have the PHP short opening and
168
            // closing tags every so often so we skip those and only
169
            // do the long opening tags.
170
            $str = preg_replace('/<\?(php)/i', "&lt;?\\1", $str);
171
        } else {
172
            $str = str_replace(['<?', '?' . '>'], ['&lt;?', '?&gt;'], $str);
173
        }
174
175
        /*
176
           * Compact any exploded words
177
           *
178
           * This corrects words like:  j a v a s c r i p t
179
           * These words are compacted back to their correct state.
180
           */
181
        $words = [
182
            'javascript',
183
            'expression',
184
            'vbscript',
185
            'script',
186
            'applet',
187
            'alert',
188
            'document',
189
            'write',
190
            'cookie',
191
            'window'
192
        ];
193
194
        foreach ($words as $word) {
195
            $temp = '';
196
197
            for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++) {
198
                $temp .= substr($word, $i, 1) . "\s*";
199
            }
200
201
            // We only want to do this when it is followed by a non-word character
202
            // That way valid stuff like "dealer to" does not become "dealerto"
203
            $str = preg_replace_callback('#(' . substr($temp, 0, -3) . ')(\W)#is', [$this, '_compact_exploded_words'], $str);
204
        }
205
206
        /*
207
           * Remove disallowed Javascript in links or img tags
208
           * We used to do some version comparisons and use of stripos for PHP5,
209
           * but it is dog slow compared to these simplified non-capturing
210
           * preg_match(), especially if the pattern exists in the string
211
           */
212
        do {
213
            $original = $str;
214
215
            if (preg_match("/<a/i", $str)) {
216
                $str = preg_replace_callback("#<a\s+([^>]*?)(>|$)#si", [$this, '_js_link_removal'], $str);
217
            }
218
219
            if (preg_match("/<img/i", $str)) {
220
                $str = preg_replace_callback("#<img\s+([^>]*?)(\s?/?>|$)#si", [$this, '_js_img_removal'], $str);
221
            }
222
223
            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...
224
                $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str);
225
            }
226
        } while ($original != $str);
227
228
        unset($original);
229
230
        // Remove evil attributes such as style, onclick and xmlns
231
        $str = $this->_remove_evil_attributes($str, $is_image, $evilAttribute);
232
233
        /*
234
           * Sanitize naughty HTML elements
235
           *
236
           * If a tag containing any of the words in the list
237
           * below is found, the tag gets converted to entities.
238
           *
239
           * So this: <blink>
240
           * Becomes: &lt;blink&gt;
241
           */
242
        $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';
243
        $str     = preg_replace_callback('#<(/*\s*)(' . $naughty . ')([^><]*)([><]*)#is', [$this, '_sanitize_naughty_html'], $str);
244
245
        /*
246
           * Sanitize naughty scripting elements
247
           *
248
           * Similar to above, only instead of looking for
249
           * tags it looks for PHP and JavaScript commands
250
           * that are disallowed.  Rather than removing the
251
           * code, it simply converts the parenthesis to entities
252
           * rendering the code un-executable.
253
           *
254
           * For example:	eval('some code')
255
           * Becomes:		eval&#40;'some code'&#41;
256
           */
257
        $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);
258
259
260
        // Final clean up
261
        // This adds a bit of extra precaution in case
262
        // something got through the above filters
263
        $str = $this->_do_never_allowed($str);
264
265
        /*
266
           * Images are Handled in a Special Way
267
           * - Essentially, we want to know that after all of the character
268
           * conversion is done whether any unwanted, likely XSS, code was found.
269
           * If not, we return TRUE, as the image is clean.
270
           * However, if the string post-conversion does not matched the
271
           * string post-removal of XSS, then it fails, as there was unwanted XSS
272
           * code found and removed/changed during processing.
273
           */
274
275
        if ($is_image === true) {
276
            return ($str == $converted_string) ? true : false;
277
        }
278
279
        return $str;
280
    }
281
282
    // --------------------------------------------------------------------
283
284
    /*
285
      * Remove Evil HTML Attributes (like evenhandlers and style)
286
      *
287
      * It removes the evil attribute and either:
288
      * 	- Everything up until a space
289
      *		For example, everything between the pipes:
290
      *		<a |style=document.write('hello');alert('world');| class=link>
291
      * 	- Everything inside the quotes
292
      *		For example, everything between the pipes:
293
      *		<a |style="document.write('hello'); alert('world');"| class="link">
294
      *
295
      * @param string $str The string to check
296
      * @param boolean $is_image TRUE if this is an image
297
      * @return string The string with the evil attributes removed
298
      */
299
    protected function _remove_evil_attributes($str, $is_image, $evilAttribute)
300
    {
301
        // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns
302
        if ($evilAttribute) {
303
            $evil_attributes = ['on\w*', 'style', 'xmlns'];
304
        } else {
305
            $evil_attributes = ['on\w*', 'xmlns'];
306
        }
307
308
        if ($is_image === true) {
309
            /*
310
                * Adobe Photoshop puts XML metadata into JFIF images,
311
                * including namespacing, so we have to allow this for images.
312
                */
313
            unset($evil_attributes[array_search('xmlns', $evil_attributes)]);
314
        }
315
316
        do {
317
            $str = preg_replace(
318
                "#<(/?[^><]+?)([^A-Za-z\-])(" . implode('|', $evil_attributes) . ")(\s*=\s*)([\"][^>]*?[\"]|[\'][^>]*?[\']|[^>]*?)([\s><])([><]*)#i",
319
                "<$1$6",
320
                $str, -1, $count
321
            );
322
        } 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...
323
324
        return $str;
325
    }
326
327
328
329
    // --------------------------------------------------------------------
330
331
    /**
332
     * HTML Entities Decode
333
     *
334
     * This function is a replacement for html_entity_decode()
335
     *
336
     * The reason we are not using html_entity_decode() by itself is because
337
     * while it is not technically correct to leave out the semicolon
338
     * at the end of an entity most browsers will still interpret the entity
339
     * correctly.  html_entity_decode() does not convert entities without
340
     * semicolons, so we are left with our own little solution here. Bummer.
341
     *
342
     * @param    string
343
     * @param    string
344
     * @return    string
345
     */
346
    public function entity_decode($str, $charset = 'UTF-8')
347
    {
348
        if (stristr($str, '&') === false) {
349
            return $str;
350
        }
351
352
        $str = html_entity_decode($str, ENT_COMPAT, $charset);
353
        $str = preg_replace_callback('~&#x(0*[0-9a-f]{2,5})~i', function($matches) {
354
            return chr(hexdec($matches[1]));
355
        }, $str);
356
        return preg_replace_callback('~&#([0-9]{2,4})~', function($matches) {
357
            return chr($matches[1]);
358
        }, $str);
359
    }
360
361
    // --------------------------------------------------------------------
362
363
    /**
364
     * Filename Security
365
     *
366
     * @param    string
367
     * @param    bool
368
     * @return    string
369
     */
370
    public function sanitize_filename($str, $relative_path = false)
371
    {
372
        $bad = [
373
            "../",
374
            "<!--",
375
            "-->",
376
            "<",
377
            ">",
378
            "'",
379
            '"',
380
            '&',
381
            '$',
382
            '#',
383
            '{',
384
            '}',
385
            '[',
386
            ']',
387
            '=',
388
            ';',
389
            '?',
390
            "%20",
391
            "%22",
392
            "%3c",        // <
393
            "%253c",    // <
394
            "%3e",        // >
395
            "%0e",        // >
396
            "%28",        // (
397
            "%29",        // )
398
            "%2528",    // (
399
            "%26",        // &
400
            "%24",        // $
401
            "%3f",        // ?
402
            "%3b",        // ;
403
            "%3d"        // =
404
        ];
405
406
        if (!$relative_path) {
407
            $bad[] = './';
408
            $bad[] = '/';
409
        }
410
411
        $str = $this->remove_invisible_characters($str, false);
412
413
        return stripslashes(str_replace($bad, '', $str));
414
    }
415
416
    // ----------------------------------------------------------------
417
418
    /**
419
     * Compact Exploded Words
420
     *
421
     * Callback function for xss_clean() to remove whitespace from
422
     * things like j a v a s c r i p t
423
     *
424
     * @param    type
425
     * @return    type
426
     */
427
    protected function _compact_exploded_words($matches)
428
    {
429
        return preg_replace('/\s+/s', '', $matches[1]) . $matches[2];
430
    }
431
432
    // --------------------------------------------------------------------
433
434
    /**
435
     * Sanitize Naughty HTML
436
     *
437
     * Callback function for xss_clean() to remove naughty HTML elements
438
     *
439
     * @param    array
440
     * @return    string
441
     */
442
    protected function _sanitize_naughty_html($matches)
443
    {
444
        // encode opening brace
445
        $str = '&lt;' . $matches[1] . $matches[2] . $matches[3];
446
447
        // encode captured opening or closing brace to prevent recursive vectors
448
        $str .= str_replace(['>', '<'], ['&gt;', '&lt;'],
449
            $matches[4]);
450
451
        return $str;
452
    }
453
454
    // --------------------------------------------------------------------
455
456
    /**
457
     * JS Link Removal
458
     *
459
     * Callback function for xss_clean() to sanitize links
460
     * This limits the PCRE backtracks, making it more performance friendly
461
     * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
462
     * PHP 5.2+ on link-heavy strings
463
     *
464
     * @param    array
465
     * @return    string
466
     */
467 View Code Duplication
    protected function _js_link_removal($match)
468
    {
469
        return str_replace(
470
            $match[1],
471
            preg_replace(
472
                '#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
473
                '',
474
                $this->_filter_attributes(str_replace(['<', '>'], '', $match[1]))
475
            ),
476
            $match[0]
477
        );
478
    }
479
480
    // --------------------------------------------------------------------
481
482
    /**
483
     * JS Image Removal
484
     *
485
     * Callback function for xss_clean() to sanitize image tags
486
     * This limits the PCRE backtracks, making it more performance friendly
487
     * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
488
     * PHP 5.2+ on image tag heavy strings
489
     *
490
     * @param    array
491
     * @return    string
492
     */
493 View Code Duplication
    protected function _js_img_removal($match)
494
    {
495
        return str_replace(
496
            $match[1],
497
            preg_replace(
498
                '#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
499
                '',
500
                $this->_filter_attributes(str_replace(['<', '>'], '', $match[1]))
501
            ),
502
            $match[0]
503
        );
504
    }
505
506
    // --------------------------------------------------------------------
507
508
    /**
509
     * Attribute Conversion
510
     *
511
     * Used as a callback for XSS Clean
512
     *
513
     * @param    array
514
     * @return    string
515
     */
516
    protected function _convert_attribute($match)
517
    {
518
        return str_replace(['>', '<', '\\'], ['&gt;', '&lt;', '\\\\'], $match[0]);
519
    }
520
521
    // --------------------------------------------------------------------
522
523
    /**
524
     * Filter Attributes
525
     *
526
     * Filters tag attributes for consistency and safety
527
     *
528
     * @param    string
529
     * @return    string
530
     */
531
    protected function _filter_attributes($str)
532
    {
533
        $out = '';
534
535
        if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) {
536
            foreach ($matches[0] as $match) {
537
                $out .= preg_replace("#/\*.*?\*/#s", '', $match);
538
            }
539
        }
540
541
        return $out;
542
    }
543
544
    // --------------------------------------------------------------------
545
546
    /**
547
     * HTML Entity Decode Callback
548
     *
549
     * Used as a callback for XSS Clean
550
     *
551
     * @param    array
552
     * @return    string
553
     */
554
    protected function _decode_entity($match)
555
    {
556
        return $this->entity_decode($match[0]);
557
    }
558
559
    // --------------------------------------------------------------------
560
561
    /**
562
     * Validate URL entities
563
     *
564
     * Called by xss_clean()
565
     *
566
     * @param    string
567
     * @return    string
568
     */
569
    protected function _validate_entities($str)
570
    {
571
        /*
572
         * Protect GET variables in URLs
573
         */
574
575
        // 901119URL5918AMP18930PROTECT8198
576
577
        $str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash() . "\\1=\\2", $str);
578
579
        /*
580
         * Validate standard character entities
581
         *
582
         * Add a semicolon if missing.  We do this to enable
583
         * the conversion of entities to ASCII later.
584
         *
585
         */
586
        $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str);
587
588
        /*
589
         * Validate UTF16 two byte encoding (x00)
590
         *
591
         * Just as above, adds a semicolon if missing.
592
         *
593
         */
594
        $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i', "\\1\\2;", $str);
595
596
597
        return $str;
598
    }
599
600
    // ----------------------------------------------------------------------
601
602
    /**
603
     * Do Never Allowed
604
     *
605
     * A utility function for xss_clean()
606
     *
607
     * @param    string
608
     * @return    string
609
     */
610
    protected function _do_never_allowed($str)
611
    {
612
        $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);
613
614
        foreach ($this->_never_allowed_regex as $regex) {
615
            $str = preg_replace('#' . $regex . '#is', '[removed]', $str);
616
        }
617
618
        return $str;
619
    }
620
621
622
    protected function remove_invisible_characters($str, $url_encoded = true)
623
    {
624
        $non_displayables = [];
625
626
        // every control character except newline (dec 10)
627
        // 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...
628
629
        if ($url_encoded) {
630
            $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...
631
            $non_displayables[] = '/%1[0-9a-f]/';    // url encoded 16-31
632
        }
633
634
        $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...
635
636
        do {
637
            $str = preg_replace($non_displayables, '', $str, -1, $count);
638
        } 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...
639
640
        return $str;
641
    }
642
643
    /**
644
     * Random Hash for protecting URLs
645
     *
646
     * @return    string
647
     */
648
    public function xss_hash()
649
    {
650
        if ($this->_xss_hash == '') {
651
            mt_srand();
652
            $this->_xss_hash = md5(time() + mt_rand(0, 1999999999));
653
        }
654
655
        return $this->_xss_hash;
656
    }
657
658
659 6
    public static function escapeLike($str, $escape = '\'\'')
660
    {
661
662 6
        return str_replace(
663 6
            ['%', '_', '\'', '"', '<', '>', '(', ')', '{', ']', ':', '/', '_', '\\'],
664 6
            ['\%', '\_', $escape, '\"', '\<', '\>', '\(', '\)', '\{', '\}', '\:', '\/', '\_', '\\\\'], $str);
665
    }
666
}