Как писать безопасный SQL в модулях OpenCart
SQL-инъекция — уязвимость, при которой злоумышленник может выполнить произвольный SQL-запрос к базе данных. В OpenCart, где многие модули пишут разработчики разного уровня, это одна из самых частых проблем безопасности. Одна неправильная строка в модуле — и вся база данных открыта для взлома.
Из практики: правильная архитектура модуля экономит дни поддержки.
В этой статье — четыре правила безопасного SQL в OpenCart, типовые ошибки и как их избежать.
Правило 1. Всегда экранируйте строки
Если вы подставляете пользовательские данные (название товара, имя покупателя, комментарий) в SQL-запрос — используйте $this->db->escape($value):
// Неправильно — SQL-инъекция!
$name = $this->request->post['name'];
$this->db->query("SELECT * FROM " . DB_PREFIX . "product WHERE name = '$name'");
// Правильно — данные экранированы
$name = $this->db->escape($this->request->post['name']);
$this->db->query("SELECT * FROM " . DB_PREFIX . "product WHERE name = '$name'");
Что делает escape(): экранирует кавычки и специальные символы, которые могут быть использованы для SQL-инъекции.
Правило 2. Приводите числа к int
Для числовых параметров (ID товара, количество, цена) используйте приведение типа — это проще и надёжнее экранирования:
// Правильно — число приведено к int
$product_id = (int)$this->request->get['product_id'];
$this->db->query("SELECT * FROM " . DB_PREFIX . "product WHERE product_id = $product_id");
// Неправильно — строка подставляется без приведения
$product_id = $this->request->get['product_id'];
$this->db->query("SELECT * FROM " . DB_PREFIX . "product WHERE product_id = $product_id");
Правило 3. Не конкатенируйте имена таблиц и колонок
Имя таблицы или колонки не должно приходить из пользовательского ввода. Если нужно динамическое имя — используйте белый список:
// Неправильно — SQL-инъекция через имя колонки
$column = $this->request->get['sort'];
$this->db->query("SELECT * FROM " . DB_PREFIX . "product ORDER BY $column");
// Правильно — белый список допустимых значений
$allowed = ['name', 'price', 'date_added', 'model'];
$column = in_array($this->request->get['sort'], $allowed) ? $this->request->get['sort'] : 'name';
$this->db->query("SELECT * FROM " . DB_PREFIX . "product ORDER BY $column");
Правило 4. Всегда используйте DB_PREFIX
В OpenCart таблицы имеют префикс (например, oc_product). Всегда используйте константу DB_PREFIX:
// Правильно
$this->db->query("SELECT * FROM " . DB_PREFIX . "product WHERE product_id = $product_id");
// Неправильно — будет работать только с префиксом oc_
$this->db->query("SELECT * FROM oc_product WHERE product_id = $product_id");
Типовые ошибки
| Ошибка | Пример | Решение |
|---|---|---|
| Пропущен escape() | WHERE name = '$name' |
Используйте $this->db->escape() |
| LIKE без экранирования | WHERE name LIKE '%$search%' |
Экранируйте % и _ отдельно |
| Число не приведено к int | WHERE id = $id |
Используйте (int)$id |
| Имя колонки из ввода | ORDER BY $column |
Белый список допустимых значений |
| Нет DB_PREFIX | FROM product |
Используйте DB_PREFIX . 'product' |
Как проверить модуль на SQL-инъекции
- Найдите все вызовы
$this->db->query()в коде модуля. - Проверьте каждый запрос: все ли переменные экранированы?
- Проверьте числовые параметры: приведены ли к int?
- Проверьте имена таблиц/колонок: не приходят ли из пользовательского ввода?
- Проверьте LIKE-запросы: экранированы ли спецсимволы?
Чек-лист безопасности
- Все строковые параметры экранированы через
$this->db->escape() - Все числовые параметры приведены к
(int) - Имена таблиц/колонок не приходят из пользовательского ввода
- Все запросы используют
DB_PREFIX - LIKE-запросы экранируют % и _
- Нет динамического построения SQL без белого списка
Рекомендации из практики
Как лучше: Всегда используйте (int) для числовых параметров из URL — это самый простой и надёжный способ защиты. ID товара, ID категории, лимит, страница — любые числа должны проходить через (int). По практике, большинства SQL-инъекций в OpenCart-модулях происходят именно через незащищённые числовые параметры.
Как не делать: Не экранируйте числа через escape() — это избыточно. Для чисел достаточно (int), для строк — escape. Смешение подходов — частая ошибка новичков.
Кейс из практики: Аудит OpenCart-магазина показал, что в кастомном модуле фильтров SQL-запрос собирался через конкатенацию. Через параметр cat=1 UNION SELECT можно было вытянуть базу пользователей. Исправили за несколько минут: добавили (int) к параметру.
Нужен аудит безопасности — закажите технический аудит: проверим SQL-запросы и модули на уязвимости.
Частые вопросы
OpenCart уже защищает от SQL-инъекций?
Частично. Метод $this->db->escape() экранирует строки, но не защищает от ошибок разработчика. Если разработчик забыл вызвать escape() или неправильно привёл число — уязвимость остаётся. Проверка модулей — ответственность разработчика.
Как быстро проверить модуль на SQL-инъекции?
Найдите все вызовы $this->db->query() (grep по коду модуля) и проверьте каждый запрос по чек-листу выше. Обычно это 10–20 запросов в модуле — проверка занимает 15–30 минут.
Что делать, если нашёл SQL-инъекцию в модуле?
Если модуль поддерживается — сообщите автору. Если модуль не поддерживается — исправьте самостоятельно (или попросите разработчика) по правилам выше. Не используйте модуль с известной уязвимостью — это риск для всего сайта.
Как проверить все модули на SQL-инъекции разом?
Используйте grep по папке модулей. Все найденные места, где GET-параметр попадает в SQL-запрос без (int) или escape — потенциальная уязвимость. По практике, этой проверкой закрывается большинство проблем безопасности.
Связанные темы
- Безопасность OpenCart: топ-10 уязвимостей — обзор основных проблем безопасности
- Как защитить админку OpenCart от взлома — защита панели управления
- WMS и маркировка — автоматизация склада
Безопасный SQL — это не опция, а обязательное требование для каждого модуля. Если нужно проверить модуль на уязвимости или написать безопасный код — доработка OpenCart это наш профиль.
Комментарии (0)
Пока нет комментариев. Будьте первым!
Оставить комментарий