Completed
Pull Request — master (#568)
by Michael
06:11
created

ProtectorMySQLDatabase::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
/**
13
 * Protector
14
 *
15
 * @copyright       XOOPS Project (http://xoops.org)
16
 * @license         GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
17
 * @package         protector
18
 * @author          trabis <[email protected]>
19
 * @version         $Id$
20
 */
21
22
$root_path = \XoopsBaseConfig::get('root-path');
23
$db_type = \XoopsBaseConfig::get('db-type');
24
25
if (XoopsLoad::fileExists($root_path.'/class/database/drivers/'.$db_type.'/database.php')) {
26
    require_once $root_path.'/class/database/drivers/'.$db_type.'/database.php';
27
} else {
28
    require_once $root_path.'/class/database/'.$db_type.'database.php';
29
}
30
31
require_once $root_path.'/class/database/database.php' ;
32
33
class ProtectorMySQLDatabase extends XoopsMySQLDatabaseProxy
0 ignored issues
show
Deprecated Code introduced by
The class XoopsMySQLDatabaseProxy has been deprecated: since version 2.6.0 - alpha 3. Switch to doctrine connector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

33
class ProtectorMySQLDatabase extends /** @scrutinizer ignore-deprecated */ XoopsMySQLDatabaseProxy
Loading history...
34
{
35
    public $doubtful_requests = array() ;
36
    public $doubtful_needles = array(
37
    // 'order by' ,
38
    'concat' ,
39
    'information_schema' ,
40
    'select' ,
41
    'union' ,
42
    '/*' , /**/
43
    '--' ,
44
    '#' ,
45
) ;
46
47
48
    public function __construct()
49
    {
50
        $protector = Protector::getInstance() ;
51
        $this->doubtful_requests = $protector->getDblayertrapDoubtfuls() ;
52
        $this->doubtful_needles = array_merge($this->doubtful_needles, $this->doubtful_requests) ;
53
    }
54
55
56
    public function injectionFound($sql)
57
    {
58
        $protector = Protector::getInstance() ;
59
60
        $protector->last_error_type = 'SQL Injection' ;
61
        $protector->message .= $sql ;
62
        $protector->output_log($protector->last_error_type) ;
63
        die('SQL Injection found') ;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
64
    }
65
66
67
    public function separateStringsInSQL($sql)
68
    {
69
        $sql = trim($sql) ;
70
        $sql_len = strlen($sql) ;
71
        $char = '' ;
0 ignored issues
show
Unused Code introduced by
The assignment to $char is dead and can be removed.
Loading history...
72
        $string_start = '' ;
73
        $in_string = false;
74
        $sql_wo_string = '' ;
75
        $strings = array() ;
76
        $current_string = '' ;
77
78
        for ($i = 0 ; $i < $sql_len ; ++$i) {
79
            $char = $sql[$i] ;
80
            if ($in_string) {
81
                while (1) {
82
                    $new_i = strpos($sql, $string_start, $i) ;
83
                    $current_string .= substr($sql, $i, $new_i - $i + 1) ;
84
                    $i = $new_i ;
85
                    if ($i === false) {
86
                        break 2 ;
87
                    } elseif ( /* $string_start == '`' || */ $sql[$i-1] !== '\\') {
88
                        $string_start = '' ;
89
                        $in_string = false ;
90
                        $strings[] = $current_string ;
91
                        break ;
92
                    } else {
93
                        $j = 2 ;
94
                        $escaped_backslash = false ;
95
                        while ($i - $j > 0 && $sql[$i-$j] === '\\') {
96
                            $escaped_backslash = ! $escaped_backslash ;
97
                            ++$j;
98
                        }
99
                        if ($escaped_backslash) {
100
                            $string_start = '' ;
101
                            $in_string = false ;
102
                            $strings[] = $current_string ;
103
                            break ;
104
                        } else {
105
                            ++$i;
106
                        }
107
                    }
108
                }
109
            } elseif ($char === '"' || $char === "'") { // dare to ignore ``
110
                $in_string = true ;
111
                $string_start = $char ;
112
                $current_string = $char ;
113
            } else {
114
                $sql_wo_string .= $char ;
115
            }
116
            // dare to ignore comment
117
        // because unescaped ' or " have been already checked in stage1
118
        }
119
120
        return array( $sql_wo_string , $strings ) ;
121
    }
122
123
124
125
    /**
126
     * @param string $sql
127
     */
128
    public function checkSql($sql)
129
    {
130
        list($sql_wo_strings, $strings) = $this->separateStringsInSQL($sql) ;
131
132
        // stage1: addslashes() processed or not
133
        foreach ($this->doubtful_requests as $request) {
134
            if (addslashes($request) != $request) {
135
                if (stristr($sql, trim($request))) {
136
                    // check the request stayed inside of strings as whole
137
                    $ok_flag = false ;
138
                    foreach ($strings as $string) {
139
                        if (strstr($string, $request)) {
140
                            $ok_flag = true ;
141
                            break ;
142
                        }
143
                    }
144
                    if (! $ok_flag) {
145
                        $this->injectionFound($sql) ;
146
                    }
147
                }
148
            }
149
        }
150
151
        // stage2: doubtful requests exists and outside of quotations ('or")
152
        // $_GET['d'] = '1 UNION SELECT ...'
153
        // NG: select a from b where c=$d
154
        // OK: select a from b where c='$d_escaped'
155
        // $_GET['d'] = '(select ... FROM)'
156
        // NG: select a from b where c=(select ... from)
157
        foreach ($this->doubtful_requests as $request) {
158
            if (strstr($sql_wo_strings, trim($request))) {
159
                $this->injectionFound($sql) ;
160
            }
161
        }
162
163
        // stage3: comment exists or not without quoted strings (too sensitive?)
164
        if (preg_match('/(\/\*|\-\-|\#)/', $sql_wo_strings, $regs)) {
165
            foreach ($this->doubtful_requests as $request) {
166
                if (strstr($request, $regs[1])) {
167
                    $this->injectionFound($sql) ;
168
                }
169
            }
170
        }
171
    }
172
173
174
    public function query($sql, $limit = 0, $start = 0)
175
    {
176
        $sql4check = substr($sql, 7) ;
177
        foreach ($this->doubtful_needles as $needle) {
178
            if (stristr($sql4check, $needle)) {
179
                $this->checkSql($sql) ;
180
                break ;
181
            }
182
        }
183
184
        if (! defined('XOOPS_DB_PROXY')) {
185
            $ret = parent::queryF($sql, $limit, $start) ;
0 ignored issues
show
Deprecated Code introduced by
The function XoopsMySQLDatabase::queryF() has been deprecated: since version 2.6.0 - alpha 3. Switch to doctrine connector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

185
            $ret = /** @scrutinizer ignore-deprecated */ parent::queryF($sql, $limit, $start) ;

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
186
        } else {
187
            $ret = parent::query($sql, $limit, $start) ;
0 ignored issues
show
Deprecated Code introduced by
The function XoopsMySQLDatabaseProxy::query() has been deprecated: since version 2.6.0 - alpha 3. Switch to doctrine connector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

187
            $ret = /** @scrutinizer ignore-deprecated */ parent::query($sql, $limit, $start) ;

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
188
        }
189
        return $ret ;
190
    }
191
}
192