Откажитесь от конкатенации запросов. Используйте подготовленные выражения через PDO с параметризацией. Это исключает возможность внедрения управляющих конструкций в запрос к базе данных. Пример: $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
.
Фильтрация на входе и проверка на выходе – подход, который должен стать привычкой. Проверяйте данные по типу: число должно быть числом, email – email. Для проверки используйте filter_var() и строгие регулярные выражения.
Ограничьте права учетной записи, через которую приложение работает с БД. У этой роли не должно быть доступа к операциям DROP, ALTER, GRANT и другим административным командам. Это минимизирует ущерб при успешной попытке внедрения.
Устанавливайте заголовки Content-Security-Policy для ограничения источников скриптов. Это один из способов блокировать внедренный код даже при его наличии в HTML. Дополнительно применяйте X-Content-Type-Options: nosniff и X-Frame-Options: DENY.
Не забывайте обновлять сторонние библиотеки. Уязвимости часто появляются не в вашем коде, а в зависимостях. Используйте composer audit и мониторинг GitHub Security Advisories.
Если приходится использовать HTML, подключи библиотеку вроде HTMLPurifier и заранее настрой допустимые теги и атрибуты. Не пиши собственные фильтры – это ловушка, в которой легко пропустить кейс.
Запрещай вставку HTML в текстовые поля, если это не нужно. Даже комментарии, описания и формы обратной связи не должны принимать «чистый» HTML. Валидация на клиенте не спасёт – обрабатывай всё на сервере.
Не вставляй данные внутрь атрибутов, классов, inline-стилей и JavaScript без строгой фильтрации. Особенно опасны конструкции типа onclick
, href="javascript:"
и вставки через innerHTML
.
Периодически проверяй отчёты Content Security Policy. Включи CSP заголовок хотя бы в режиме Content-Security-Policy-Report-Only
– он поможет найти потенциальные точки внедрения.
Методы защиты от SQL-инъекций при работе с базами данных в PHP
Используйте подготовленные выражения (prepared statements). Вместо подстановки переменных напрямую в запрос, передавайте значения отдельно. Это исключает влияние пользовательского ввода на структуру запроса. В PDO – метод prepare()
и execute()
. В MySQLi – prepare()
и bind_param()
.
Пример с использованием PDO:
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute(['email' => $_POST['email']]);
$data = $stmt->fetch();
Никогда не вставляйте данные напрямую в SQL-строку. Даже если вы уверены в источнике данных. Проверка типа переменной – не защита. Подготовленные выражения работают как фильтр на уровне протокола.
Что еще стоит учесть:
Отключите эмуляцию подготовленных запросов в PDO. По умолчанию они эмулируются, что снижает уровень изоляции данных. Пропишите:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Ограничьте права пользователя базы данных. У аккаунта, с которого работает приложение, не должно быть доступа к командам DROP
, DELETE
без условий, и другим потенциально разрушительным действиям. Назначьте минимально необходимые привилегии.
Если используете ORM, убедитесь, что он поддерживает привязку параметров и не вставляет значения напрямую. Не доверяйте автоформированию запросов без явного контроля над входными данными.
И, наконец, не забывайте логировать подозрительные запросы, особенно те, где количество переданных параметров не совпадает с ожидаемым.