Как проверить работоспособность SQL-запроса в PHP - PullRequest
0 голосов
/ 27 января 2019

Я пишу DbAdapter на PHP.Чтобы избежать атак с использованием SQL-инъекций, для условного выбора мне нужен способ проверить работоспособность SQL-запроса, который я собираюсь выполнить.Учитывая, что подготовленные операторы очень усложняют реализацию, есть ли быстрый способ проверить работоспособность SQL-запроса (в частности, WHERE предложений, как в данном случае) перед выполнением в самом сердце класса?Например, вспомогательный метод для возврата false для вредоносных или подозрительных запросов будет в порядке.

Код моего класса:

require_once './config.php';

class DbAdapter
{

    private $link;


    /**
     * DbAdapter constructor.
     */
    public function __construct()
    {
        $this->link = new mysqli(DBHOST, DBUSER, DBPASS, DBNAME);
        if ($this->link->connect_errno) {
            die($this->link->connect_error);
        }
    }

    /**
     * @param $table
     * @param array $columns
     * @param string $condition
     * @return bool|mysqli_result
     */
    public function select($table, $columns = [], $condition = "")
    {
        $colsString = $this->extractCols($columns);
        $whereString = $this->extractConditions($condition);
        $sql = "SELECT $colsString FROM `$table` " . $whereString;
        return $this->link->query($sql);
    }

    public function __destruct()
    {
       $this->link->close();
    }

    private function extractCols(array $columns)
    {
        if(!$columns) { return '*';}
        else {
            $str = "";
            foreach($columns as $col) {
                $str .= "$col,";
            }
            return trim($str, ',');
        }
    }

    private function extractConditions(string $conditions)
    {
        if(!$conditions) {
            return "";
        }
        else {
            $where = "WHERE ";
            foreach ($conditions as $key => $value){
                $where .= "$key=" . $conditions[$key] . "&";
            }

            return trim($where, "&");
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Разрешение произвольного ввода стать частью вашего кода SQL является принципиально ошибочным проектом.Нет никакого способа сделать это "вменяемым".

Некоторые технологии, такие как Брандмауэр базы данных , пытаются сделать то, что вы просите, чтобы обнаружить, когда запросы скомпрометированы атакой внедрения SQL.Проблема в том, что очень трудно отличить скомпрометированный запрос SQL от запроса, который просто включает в себя законный динамический контент.

В результате методы обнаружения инъекций ненадежны.Им не удается обнаружить все случаи внедрения, а также они ошибочно идентифицируются как допустимые случаи внедрения.

Другой подход заключается в использовании белого списка SQL-запросов.То есть перечислите все допустимые формы запросов SQL, используемые данным приложением, и разрешите запуск только этих запросов.Это требует, чтобы вы запускали приложение в своего рода «режиме обучения» перед развертыванием, чтобы идентифицировать все допустимые запросы SQL.Затем включите брандмауэр базы данных, чтобы заблокировать все, что не было известным запросом SQL во время выполнения теста.

Это также имеет недостатки.Он не учитывает запросы SQL, которые должны быть полностью динамическими, например запросы сводной таблицы или конструктивные условия (например, ваш запрос получает переменное число терминов в предложении WHERE на основе логики приложения).

ЛучшийМетод предотвращения внедрения SQL-кода по-прежнему заключается в использовании обзора кода.Убедитесь, что любые динамические значения передаются в качестве параметров запроса с использованием подготовленного оператора.Вы утверждаете, что это делает код «очень сложным», но это не так.

$sql = "SELECT ...";
$stmt = $pdo->prepare($sql);
$stmt->execute($paramValuesArray);

По крайней мере, мы можем сказать, что это не менее сложный способ написания всего кода, который вы показали, который добавляет термины в оператор SQL.

0 голосов
/ 27 января 2019

Короткий ответ

Вы можете использовать EXPLAIN, как в EXPLAIN SELECT foo FROM table_bar.Как интерпретировать результаты программно для «здравомыслия», однако, является гораздо более сложным вопросом.Вам понадобится программное определение «здравомыслие», например «проверяет более n строк» ​​или «включает в себя более t таблиц».

SQL-инъекция

Вы упомянули, что ваша мотивация включает в себя желание «избежать атак SQL инъекций».Если это то, что вас беспокоит, самое важное здесь - избегать объединения любых пользовательских данных в запрос. SQL-инъекция возможна, если вы объединяете любые пользовательских данных, и их очень, очень трудно обнаружить.Гораздо лучше, просто полностью предотвратить его.

Этот код, честно говоря, заставляет мои волосы встать дыбом:

$where = "WHERE ";
foreach ($conditions as $key => $value){
    $where .= "$key=" . $conditions[$key] . "&";
}

Нет способа сделать это достаточно безопасным или проверить его на предмет здравомыслия,Вы можете подумать: «Да, но все условия должны содержать только цифры», или что-то подобное, что легко проверить, но вы не можете безопасно полагаться на это.Что происходит, когда вы изменяете свой код в следующем году, или на следующей неделе, или завтра, и добавляете строковый параметр?Мгновенная уязвимость.

Вам нужно использовать подготовленные операторы, а не объединять переменные в запросе.Простого экранирования переменных недостаточно.См. Как я могу предотвратить внедрение SQL в PHP? .

Некоторые примечания по проектированию приложений

Обратите внимание, что это обычно то, что вы делаете перед развертыванием запросов в производство, а не на лету.Если вы создаете плату, позволяющую пользователям создавать свои собственные запросы, некоторая оценка запросов на лету может быть неизбежной.

Но если все, с чем вы сталкиваетесь, это множественные условия в WHERE, тогда запросы будут быстрыми (и вам не нужно будет использовать EXPLAIN), если выполняются две вещи:

  1. вы не используете подзапросы, такие как ... WHERE id IN (SELECT id from OtherTable WHERE ...) ...и
  2. у вас есть соответствующие индексы.(Опять же, однако, это то, что вы можете ожидать во время разработки в> 99% случаев.)

Соответствующая «Военная история», которая, надеюсь, ослабит некоторые ваши страхи

Iоднажды написал инструмент, который позволял создавать все виды сложных запросов и выполнять их с MySQL в базе данных с несколькими миллионами строк в каждой из основных таблиц.Запросы были в основном простые WHERE условия, такие как WHERE lastOrder > '2018-01-01', наряду с несколькими (в основном жестко заданными) JOIN и возможностями подзапроса.Я просто агрессивно проиндексировал, и мне никогда ничего не нужно было EXPLAIN;это никогда не выходило за рамки производительности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...