Completed
Push — work-fleets ( 1ceb9c...9cd586 )
by SuperNova.WS
04:56
created

db_mysql::security_watch_user_queries()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
rs 6.9811
cc 7
eloc 15
nc 5
nop 1
1
<?php
2
3
/**
4
 * User: Gorlum
5
 * Date: 01.09.2015
6
 * Time: 15:58
7
 */
8
class db_mysql {
9
  const DB_MYSQL_TRANSACTION_SERIALIZABLE = 'SERIALIZABLE';
10
  const DB_MYSQL_TRANSACTION_REPEATABLE_READ = 'REPEATABLE READ';
11
  const DB_MYSQL_TRANSACTION_READ_COMMITTED = 'READ COMMITTED';
12
  const DB_MYSQL_TRANSACTION_READ_UNCOMMITTED = 'READ UNCOMMITTED';
13
14
  /**
15
   * Статус соеднения с MySQL
16
   *
17
   * @var bool
18
   */
19
  public $connected = false;
20
  /**
21
   * Префикс названий таблиц в БД
22
   *
23
   * @var string
24
   */
25
  public $db_prefix = '';
26
  /**
27
   * Список таблиц в БД
28
   *
29
   * @var array
30
   */
31
  public $table_list = array();
32
33
  /**
34
   * Настройки БД
35
   *
36
   * @var array
37
   */
38
  protected $dbsettings = array();
39
  /**
40
   * Драйвер для прямого обращения к MySQL
41
   *
42
   * @var db_mysql_v5 $driver
43
   */
44
  public $driver = null;
45
46
  /**
47
   * Общее время запросов
48
   *
49
   * @var float $time_mysql_total
50
   */
51
  public $time_mysql_total = 0.0;
52
53
  public function __construct() {
54
  }
55
56
  function load_db_settings() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
57
    $dbsettings = array();
58
59
    require(SN_ROOT_PHYSICAL . "config" . DOT_PHP_EX);
60
61
    $this->dbsettings = $dbsettings;
62
  }
63
64
  function sn_db_connect($external_db_settings = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
65
    $this->db_disconnect();
66
67
    if(!empty($external_db_settings) && is_array($external_db_settings)) {
68
      $this->dbsettings = $external_db_settings;
69
    }
70
71
    if(empty($this->dbsettings)) {
72
      $this->load_db_settings();
73
    }
74
75
    // TODO - фатальные (?) ошибки на каждом шагу. Хотя - скорее Эксепшны
76
    if(!empty($this->dbsettings)) {
77
      $driver_name = empty($this->dbsettings['sn_driver']) ? 'db_mysql_v5' : $this->dbsettings['sn_driver'];
78
      $this->driver = new $driver_name();
79
      $this->db_prefix = $this->dbsettings['prefix'];
80
81
      $this->connected = $this->connected || $this->driver_connect();
82
83
      if($this->connected) {
84
        $this->table_list = $this->db_get_table_list();
85
        // TODO Проверка на пустоту
86
      }
87
    } else {
88
      $this->connected = false;
89
    }
90
91
    return $this->connected;
92
  }
93
94
  function driver_connect() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
95
    global $debug;
96
97
    if(!is_object($this->driver)) {
98
      $debug->error_fatal('DB Error - No driver for MySQL found!');
99
    }
100
101
    if(!method_exists($this->driver, 'mysql_connect')) {
102
      $debug->error_fatal('DB Error - WRONG MySQL driver!');
103
    }
104
105
    return $this->driver->mysql_connect($this->dbsettings);
106
  }
107
108
  function db_disconnect() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
109
    if($this->connected) {
110
      $this->connected = !$this->driver_disconnect();
111
      $this->connected = false;
112
    }
113
114
    return !$this->connected;
115
  }
116
117
  function doquery($query, $table = '', $fetch = false, $skip_query_check = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
118
    global $numqueries, $debug;
119
120
    if(!is_string($table)) {
121
      $fetch = $table;
122
    }
123
124
    if(!$this->connected) {
125
      $this->sn_db_connect();
126
    }
127
128
    $query = trim($query);
129
    $this->security_watch_user_queries($query);
130
    !$skip_query_check ? $this->security_query_check_bad_words($query) : false;
131
132
    $sql = $query;
133
    if(strpos($sql, '{{') !== false) {
134
      foreach($this->table_list as $tableName) {
135
        $sql = str_replace("{{{$tableName}}}", $this->db_prefix . $tableName, $sql);
136
      }
137
    }
138
139
    if(classSupernova::$config->debug) {
140
      $numqueries++;
141
      $arr = debug_backtrace();
142
      $file = end(explode('/', $arr[0]['file']));
0 ignored issues
show
Bug introduced by
explode('/', $arr[0]['file']) cannot be passed to end() as the parameter $array expects a reference.
Loading history...
143
      $line = $arr[0]['line'];
144
      $debug->add("<tr><th>Query $numqueries: </th><th>$query</th><th>$file($line)</th><th>$table</th><th>$fetch</th></tr>");
145
    }
146
147
    if(defined('DEBUG_SQL_COMMENT')) {
148
      $backtrace = debug_backtrace();
149
      $sql_comment = $debug->compact_backtrace($backtrace, defined('DEBUG_SQL_COMMENT_LONG'));
150
151
      $sql_commented = '/* ' . implode("<br />", $sql_comment) . '<br /> */ ' . preg_replace("/\s+/", ' ', $sql);
152
      if(defined('DEBUG_SQL_ONLINE')) {
153
        $debug->warning($sql_commented, 'SQL Debug', LOG_DEBUG_SQL);
154
      }
155
156
      if(defined('DEBUG_SQL_ERROR')) {
157
        array_unshift($sql_comment, preg_replace("/\s+/", ' ', $sql));
158
        $debug->add_to_array($sql_comment);
159
        // $debug->add_to_array($sql_comment . preg_replace("/\s+/", ' ', $sql));
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...
160
      }
161
      $sql = $sql_commented;
162
    }
163
164
    $sqlquery = $this->db_sql_query($sql) or $debug->error(db_error() . "<br />$sql<br />", 'SQL Error');
165
166
    return $fetch ? $this->db_fetch($sqlquery) : $sqlquery;
167
  }
168
169
170
  function security_watch_user_queries($query) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
171
    // TODO Заменить это на новый логгер
172
    global $is_watching, $user, $debug;
173
174
    if(
175
      !$is_watching // Not already watching
176
      && !empty(classSupernova::$config->game_watchlist_array) // There is some players in watchlist
177
      && in_array($user['id'], classSupernova::$config->game_watchlist_array) // Current player is in watchlist
178
      && !preg_match('/^(select|commit|rollback|start transaction)/i', $query) // Current query should be watched
179
    ) {
180
      $is_watching = true;
181
      $msg = "\$query = \"{$query}\"\n\r";
182
      if(!empty($_POST)) {
183
        $msg .= "\n\r" . dump($_POST, '$_POST');
184
      }
185
      if(!empty($_GET)) {
186
        $msg .= "\n\r" . dump($_GET, '$_GET');
187
      }
188
      $debug->warning($msg, "Watching user {$user['id']}", 399, array('base_dump' => true));
189
      $is_watching = false;
190
    }
191
  }
192
193
194
  function security_query_check_bad_words($query) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
195
    global $user, $dm_change_legit, $mm_change_legit;
196
197
    switch(true) {
198
      case stripos($query, 'RUNCATE TABL') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'RUNCATE TABL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
199
      case stripos($query, 'ROP TABL') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'ROP TABL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
200
      case stripos($query, 'ENAME TABL') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'ENAME TABL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
201
      case stripos($query, 'REATE DATABAS') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'REATE DATABAS') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
202
      case stripos($query, 'REATE TABL') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'REATE TABL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
203
      case stripos($query, 'ET PASSWOR') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'ET PASSWOR') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
204
      case stripos($query, 'EOAD DAT') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'EOAD DAT') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
205
      case stripos($query, 'RPG_POINTS') != false && stripos(trim($query), 'UPDATE ') === 0 && !$dm_change_legit:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'RPG_POINTS') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
206
      case stripos($query, 'METAMATTER') != false && stripos(trim($query), 'UPDATE ') === 0 && !$mm_change_legit:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'METAMATTER') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
207
      case stripos($query, 'AUTHLEVEL') != false && $user['authlevel'] < 3 && stripos($query, 'SELECT') !== 0:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'AUTHLEVEL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
208
        $report = "Hacking attempt (" . date("d.m.Y H:i:s") . " - [" . time() . "]):\n";
209
        $report .= ">Database Inforamation\n";
210
        $report .= "\tID - " . $user['id'] . "\n";
211
        $report .= "\tUser - " . $user['username'] . "\n";
212
        $report .= "\tAuth level - " . $user['authlevel'] . "\n";
213
        $report .= "\tAdmin Notes - " . $user['adminNotes'] . "\n";
214
        $report .= "\tCurrent Planet - " . $user['current_planet'] . "\n";
215
        $report .= "\tUser IP - " . $user['user_lastip'] . "\n";
216
        $report .= "\tUser IP at Reg - " . $user['ip_at_reg'] . "\n";
217
        $report .= "\tUser Agent- " . $_SERVER['HTTP_USER_AGENT'] . "\n";
218
        $report .= "\tCurrent Page - " . $user['current_page'] . "\n";
219
        $report .= "\tRegister Time - " . $user['register_time'] . "\n";
220
        $report .= "\n";
221
222
        $report .= ">Query Information\n";
223
        $report .= "\tQuery - " . $query . "\n";
224
        $report .= "\n";
225
226
        $report .= ">\$_SERVER Information\n";
227
        $report .= "\tIP - " . $_SERVER['REMOTE_ADDR'] . "\n";
228
        $report .= "\tHost Name - " . $_SERVER['HTTP_HOST'] . "\n";
229
        $report .= "\tUser Agent - " . $_SERVER['HTTP_USER_AGENT'] . "\n";
230
        $report .= "\tRequest Method - " . $_SERVER['REQUEST_METHOD'] . "\n";
231
        $report .= "\tCame From - " . $_SERVER['HTTP_REFERER'] . "\n";
232
        $report .= "\tPage is - " . $_SERVER['SCRIPT_NAME'] . "\n";
233
        $report .= "\tUses Port - " . $_SERVER['REMOTE_PORT'] . "\n";
234
        $report .= "\tServer Protocol - " . $_SERVER['SERVER_PROTOCOL'] . "\n";
235
236
        $report .= "\n--------------------------------------------------------------------------------------------------\n";
237
238
        $fp = fopen(SN_ROOT_PHYSICAL . 'badqrys.txt', 'a');
239
        fwrite($fp, $report);
0 ignored issues
show
Security File Manipulation introduced by
$report can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
240
        fclose($fp);
241
242
        $message = 'Привет, я не знаю то, что Вы пробовали сделать, но команда, которую Вы только послали базе данных, не выглядела очень дружественной и она была заблокированна.<br /><br />Ваш IP, и другие данные переданны администрации сервера. Удачи!.';
243
        die($message);
244
      break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
245
    }
246
  }
247
248
  /**
249
   * @param bool $prefixed_only
250
   *
251
   * @return array
252
   */
253
  function db_get_table_list($prefixed_only = true) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
254
    $query = $this->mysql_get_table_list();
255
256
    $prefix_length = strlen($this->db_prefix);
257
258
    $tl = array();
259
    while($row = $this->db_fetch($query)) {
260
      foreach($row as $table_name) {
261
        if(strpos($table_name, $this->db_prefix) === 0) {
262
          $table_name = substr($table_name, $prefix_length);
263
        } elseif($prefixed_only) {
264
          continue;
265
        }
266
        // $table_name = str_replace($db_prefix, '', $table_name);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
267
        $tl[$table_name] = $table_name;
268
      }
269
    }
270
271
    return $tl;
272
  }
273
274
  function mysql_get_table_list() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
275
    return $this->db_sql_query('SHOW TABLES;');
276
  }
277
278
  function mysql_get_innodb_status() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
279
    return $this->db_sql_query('SHOW ENGINE INNODB STATUS;');
280
  }
281
282
283
  /**
284
   * L1 perform the query
285
   *
286
   * @param $query_string
287
   *
288
   * @return bool|mysqli_result
289
   */
290 View Code Duplication
  function db_sql_query($query_string) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
    $microtime = microtime(true);
292
    $result = $this->driver->mysql_query($query_string);
293
    $this->time_mysql_total += microtime(true) - $microtime;
294
295
    return $result;
296
  }
297
298
  /**
299
   * L1 fetch assoc array
300
   *
301
   * @param $query
302
   *
303
   * @return array|null
304
   */
305 View Code Duplication
  function db_fetch(&$query) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
    $microtime = microtime(true);
307
    $result = $this->driver->mysql_fetch_assoc($query);
308
    $this->time_mysql_total += microtime(true) - $microtime;
309
310
    return $result;
311
  }
312
313
  function db_fetch_row(&$query) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
314
    return $this->driver->mysql_fetch_row($query);
315
  }
316
317
  function db_escape($unescaped_string) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
318
    return $this->driver->mysql_real_escape_string($unescaped_string);
319
  }
320
321
  function driver_disconnect() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
322
    return $this->driver->mysql_close_link();
323
  }
324
325
  function db_error() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
326
    return $this->driver->mysql_error();
327
  }
328
329
  function db_insert_id() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
330
    return $this->driver->mysql_insert_id();
331
  }
332
333
  function db_num_rows(&$result) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
334
    return $this->driver->mysql_num_rows($result);
335
  }
336
337
  function db_affected_rows() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
338
    return $this->driver->mysql_affected_rows();
339
  }
340
341
  function db_get_client_info() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
342
    return $this->driver->mysql_get_client_info();
343
  }
344
345
  function db_get_server_info() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
346
    return $this->driver->mysql_get_server_info();
347
  }
348
349
  function db_get_host_info() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
350
    return $this->driver->mysql_get_host_info();
351
  }
352
353
  function db_get_server_stat() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
354
    return $this->driver->mysql_stat();
355
  }
356
357
}
358