Уязвимости PHP: виды, примеры и защита сайта
Две одинаковые трубы разного размера: из отверстия большой падают шарики, в малой стоит зелёная заглушка

Уязвимости PHP: виды, примеры и защита сайта

Уязвимости PHP возникают из-за ошибок в коде и настройках: SQL-инъекции, XSS, include, загрузка файлов. Разбираем виды и как защитить сайт.

Уязвимости PHP: как их находят и закрывают

Уязвимость PHP — это ошибка в коде приложения, настройках сервера или устаревшей версии интерпретатора, через которую злоумышленник получает доступ к данным или запускает на сайте свой код. Сам язык не «дырявый»: большинство взломов происходит из-за непроверенного пользовательского ввода и неустановленных обновлений.

Главное правило: любые данные из $_GET, $_POST, $_COOKIE и заголовков запроса недоверенные, пока вы их не проверили.

PHP работает на огромной доле сайтов в Рунете и во всём мире: на нём построены WordPress, 1С-Битрикс, Joomla, Drupal, MODX, Laravel и Symfony. Поэтому и внимание атакующих к нему повышенное. Автоматические сканеры ищут типовые ошибки круглосуточно, и маленький сайт-визитка попадает в зону риска так же, как крупный интернет-магазин. Ниже разберём, какие уязвимости встречаются чаще всего, как они выглядят в коде и что сделать, чтобы закрыть их системно.

Откуда берутся уязвимости в PHP

Исторически PHP создавался как простой инструмент для динамических страниц, и многие старые приёмы разработки до сих пор кочуют из проекта в проект: склейка SQL-запроса из строк, вывод переменных «как есть», подключение файлов по имени из параметра. Язык позволяет писать быстро, но не заставляет делать это безопасно, поэтому ответственность лежит на разработчике.

Уязвимости обычно попадают в проект тремя путями. Первый — собственный код с ошибками валидации и экранирования. Второй — сторонние компоненты: плагины, темы, модули CMS и пакеты Composer, в которых находят критические дыры. Третий — окружение: устаревшая версия PHP, небезопасные настройки `php.ini`, лишние права у веб-сервера, открытые служебные файлы.

Важно понимать, что уязвимость сама по себе ничего не ломает. Она превращается в инцидент, когда кто-то её находит. А находят их быстро: после публикации исправления для известной дыры (CVE) боты начинают массово проверять сайты уже в течение часов или дней.

УязвимостьКак работаетЧем грозит
SQL-инъекцияДанные пользователя попадают в SQL-запрос без параметризации и меняют его смыслУтечка или удаление базы, обход авторизации
XSS (межсайтовый скриптинг)Вредоносный JavaScript выводится на страницу без экранированияКража сессий, подмена контента, редиректы на сторонние сайты
CSRFСайт принимает запрос от имени авторизованного пользователя без проверки токенаСмена пароля, оплата или удаление данных без ведома владельца аккаунта
LFI / RFI (подключение файлов)Имя файла для include или require берётся из параметра запросаЧтение системных файлов, выполнение чужого кода
Небезопасная загрузка файловСервер принимает файл с расширением .php под видом картинки или документаУстановка веб-шелла и полный контроль над сайтом
Небезопасная десериализацияunserialize() обрабатывает недоверенные данные и запускает «магические» методы объектовУдалённое выполнение кода (RCE), запись файлов на сервер

SQL-инъекции: самая известная проблема

SQL-инъекция возникает, когда значение от пользователя подставляется прямо в текст запроса. Достаточно передать в параметр `id` не число, а выражение вроде `1 OR 1=1`, и условие выборки перестаёт работать как задумано: вместо одной записи сайт отдаёт всю таблицу. В более опасных сценариях атакующий выгружает хэши паролей, меняет данные или обходит форму входа.

Технически решение известно давно и не требует сложных усилий: подготовленные выражения (prepared statements) в PDO или mysqli. Запрос и данные передаются в СУБД раздельно, поэтому значение никогда не будет воспринято как часть SQL-команды. Экранирование вручную через `addslashes` или «самописные» фильтры ненадёжны и их лучше не использовать.

01 · было · уязвимо
$id = $_GET['id'];
$sql = 'SELECT * FROM users WHERE id = ' . $id;
$result = $pdo->query($sql);

Запрос?id=1 OR 1=1вернёт всех пользователей, а специально подобранная строка позволит читать чужие таблицы.

02 · стало · безопасно
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => (int) $_GET['id']]);
$user = $stmt->fetch();

Значение передаётся отдельно от запроса и приводится к числу. Любая «хитрая» строка останется просто данными.

XSS, include и загрузка файлов

XSS появляется, когда сайт выводит в HTML строку, пришедшую от пользователя: комментарий, имя, параметр поиска. Если в ней есть тег `<script>`, браузер посетителя выполнит чужой код — отправит куки злоумышленнику или подменит форму. Защита: выводить данные через `htmlspecialchars($value, ENT_QUOTES, 'UTF-8')`, использовать шаблонизаторы с автоэкранированием (Twig, Blade) и настраивать заголовок Content-Security-Policy.

Включение файлов — следующая классика. Код вида `include $_GET['page'] . '.php';` позволяет подставить путь вроде `../../config`, а при включённой настройке `allow_url_include` — и вовсе подключить скрипт с чужого сервера. Решение: белый список допустимых страниц и полный отказ от пользовательских путей.

Загрузка файлов опасна тем, что проверка по расширению или заголовку Content-Type легко обходится. Файл `shell.php`, загруженный в публичную папку, превращается в удалённую консоль управления сайтом. Храните загрузки вне корня сайта или запретите выполнение PHP в этих каталогах, переименовывайте файлы и проверяйте реальный тип содержимого.

Отдельно стоит упомянуть сравнение через `==`. В PHP строки вида `0e12345` при нестрогом сравнении могут считаться числом ноль, поэтому два разных хэша иногда оказываются «равными». Для паролей используйте `password_hash()` и `password_verify()`, для остальных сравнений — оператор `===` и функцию `hash_equals()`.

Типичные ошибки, из-за которых PHP-сайты взламывают

Большинство инцидентов вызваны не изощрёнными атаками, а повторяющимися небрежностями при разработке и администрировании.

Так делать не стоит

  • Собирать SQL-запросы конкатенацией строк с данными из $_GET и $_POST
  • Выводить пользовательский ввод в HTML без экранирования
  • Оставлять display_errors включённым на рабочем сайте и показывать посетителям пути и тексты запросов
  • Хранить пароли в открытом виде или в виде md5 без соли
  • Держать на сервере забытые файлы: phpinfo.php, дампы базы, архивы .zip и .bak, папки vendor с тестами
  • Не обновлять PHP, CMS и плагины месяцами «чтобы ничего не сломалось»

Как правильно

  1. 01

    Использовать PDO или mysqli с подготовленными выражениями

  2. 02

    Экранировать вывод через htmlspecialchars или шаблонизатор с автоэкранированием

  3. 03

    Выключить display_errors, включить log_errors и писать ошибки в защищённый лог

  4. 04

    Хешировать пароли через password_hash() с алгоритмом по умолчанию или Argon2

  5. 05

    Регулярно чистить сервер от служебных и временных файлов, закрывать доступ к vendor и .git

  6. 06

    Обновляться по расписанию: сначала на тестовой копии, затем на рабочем сайте

Как выглядит взлом на практике

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

типовой сценарий атаки на PHP-сайт
01

Сканирование

Бот перебирает адреса и ищет версии CMS, плагины и открытые служебные файлы.

02

Эксплуатация

Через SQL-инъекцию, include или загрузку файла злоумышленник получает возможность выполнять код.

03

Веб-шелл

На сервер кладётся скрытый скрипт, который даёт постоянный доступ даже после смены паролей.

04

Монетизация

Рассылка спама, скрытые ссылки и редиректы, фишинговые страницы, майнеры, кража данных клиентов.

Устаревший PHP и громкие CVE

Отдельная категория риска — версия самого интерпретатора. Ветки PHP живут ограниченное время: сначала активная поддержка с исправлением ошибок, затем период только security-патчей, после чего поддержка прекращается. Например, PHP 7.4 перестал получать исправления в ноябре 2022 года, а PHP 5.6 — ещё в конце 2018-го. Сайты, которые продолжают на них работать, остаются с известными дырами без шансов на заплатку. Актуальные сроки поддержки смотрите на официальном сайте php.net.

Хороший пример того, как быстро развивается ситуация, — уязвимость CVE-2024-4577 в PHP-CGI на Windows (оценка CVSS 9.8). Она позволяла через специально сформированный запрос передавать аргументы интерпретатору и выполнять произвольный код. Исправления вышли в версиях 8.1.29, 8.2.20 и 8.3.8, а массовое сканирование сетей началось почти сразу после публикации деталей. Похожая история была с пакетом PHPUnit (CVE-2017-9841): забытая в рабочем окружении папка vendor открывала удалённое выполнение кода.

Мораль простая: обновления — это не опция «по возможности», а часть эксплуатации сайта. Если миграция на новую версию PHP затруднена из-за старого кода, закладывайте её в план работ, а на переходный период ограничивайте доступ к серверу и включайте дополнительную защиту на уровне веб-сервера.

План защиты PHP-сайта: 7 шагов

  1. 01

    Обновите PHP до поддерживаемой версии, а также CMS, темы, плагины и пакеты Composer. Удалите неиспользуемые расширения и модули.

  2. 02

    Валидируйте весь ввод: проверяйте тип, длину и формат, используйте filter_var() и белые списки допустимых значений.

  3. 03

    Переведите все обращения к базе на PDO или mysqli с подготовленными выражениями и выдайте пользователю БД минимально необходимые права.

  4. 04

    Экранируйте вывод, подключите CSRF-токены для всех форм с изменением данных и настройте заголовки безопасности: Content-Security-Policy, X-Content-Type-Options, Strict-Transport-Security.

  5. 05

    Настройте php.ini и веб-сервер: отключите показ ошибок, ограничьте опасные функции, запретите выполнение скриптов в папках загрузок, закройте доступ к .git, .env и резервным копиям.

  6. 06

    Включите двухфакторную аутентификацию для админки и хостинга, замените простые пароли и ограничьте число попыток входа.

  7. 07

    Настройте резервное копирование вне основного сервера, мониторинг изменений файлов и регулярные проверки: разделы безопасности в Яндекс Вебмастере и Google Search Console, анализ логов, периодическое сканирование.

Настройки php.ini, которые стоит проверить

Даже безупречный код выигрывает от правильной конфигурации. Часть настроек сразу закрывает целые классы атак или сильно усложняет их: запрет подключения удалённых файлов, ограничение каталогов, доступных скрипту, защита сессионных cookie. Ниже собран короткий чек-лист директив, которые стоит сверить на рабочем сервере. Значения подбирайте под ваш проект и проверяйте на тестовой копии, чтобы ничего не сломать.

чек-лист php.ini для рабочего сервера
expose_php = Offскрывает версию PHP в заголовках ответа
display_errors = Offпосетители не видят пути и тексты запросов; ошибки идут в лог (log_errors = On)
allow_url_include = Offзапрещает подключать скрипты по URL
open_basedirограничивает каталоги, к которым имеет доступ скрипт
disable_functionsотключает exec, system, shell_exec, passthru, proc_open, если они не нужны
session.cookie_httponly = 1закрывает доступ к сессионной cookie из JavaScript
session.cookie_secure = 1передаёт сессионную cookie только по HTTPS
session.use_strict_mode = 1защищает от подстановки чужого идентификатора сессии

Как понять, что PHP-сайт уже взломан

Явные признаки — перенаправление посетителей на чужие сайты, посторонний контент на страницах, письма от хостера о спаме или вредоносной активности. Но чаще симптомы косвенные: резкое падение трафика, появление в выдаче Яндекса и Google страниц с «левыми» заголовками (например, про казино или лекарства), рост нагрузки на сервер, незнакомые файлы с недавней датой изменения, подозрительные пользователи в админке.

Проверьте разделы безопасности в Яндекс Вебмастере и Google Search Console: поисковики сообщают о заражении и могут пометить сайт как опасный. Посмотрите отчёты Яндекс Метрики и Google Analytics на предмет странных источников трафика и аномальных переходов. Сравните файлы проекта с чистой копией или репозиторием, поищите в коде функции `eval`, `base64_decode`, `gzinflate`, `assert` рядом с данными из запросов — это типичные следы веб-шеллов.

Если сайт заражён, одной очистки файлов недостаточно: найдите и закройте уязвимость, через которую пришёл взлом, и смените все пароли и ключи — от админки, FTP/SSH, базы данных и API. Иначе заражение повторится.

Итоги: безопасность PHP — это процесс

PHP-уязвимости закрываются дисциплиной: обновления, проверка ввода, подготовленные запросы и грамотная конфигурация сервера.
  1. 01

    Основные угрозы: SQL-инъекции, XSS, CSRF, подключение файлов, загрузка файлов, десериализация

  2. 02

    Любые пользовательские данные считайте недоверенными и проверяйте их

  3. 03

    Используйте только поддерживаемые версии PHP и обновляйте CMS и плагины

  4. 04

    Настройте php.ini и права на сервере, закройте служебные файлы

  5. 05

    Следите за сигналами в Яндекс Вебмастере, Google Search Console и логах

Единожды «настроить безопасность» не получится: появляются новые CVE, меняются зависимости, разрастается код. Закладывайте регулярный аудит в жизнь проекта, и атака через типовую уязвимость превратится из реальной угрозы в маловероятный сценарий. Если нужна независимая оценка, специалисты проверят код, конфигурацию и окружение и подготовят конкретный список исправлений по приоритетам.

Часто задаваемые вопросы

Проверим ваш PHP-сайт на уязвимости до того, как это сделают хакеры

Найдём опасные места в коде и настройках и составим понятный план исправлений

Бесплатный экспресс-аудитОтчёт за 24 часаБез навязчивых звонков

Более 100 проверенных PHP-проектов

Политика конфиденциальности

При оставлении заявки на ресурсе «https://gurucontext.ru» пользователи предоставляют следующие сведения:

  • Имя
  • Контактный телефон или Telegram
  • Адрес сайта пользователя (не обязательно)

Также администрация сайта получает данные об IP-адресе посетителей, типе браузера, времени нахождения на сайте и прочие подобные сведения через сервисы статистики.

Использование информации

Вся полученная информация используется администрацией «https://gurucontext.ru» исключительно в целях связи с клиентом.

Защита персональных данных

Компания «https://gurucontext.ru» обязуется не разглашать сведения, полученные от пользователей, и хранит их в защищённом виде.

Предоставление данных третьим лицам

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

Контакты

Телефон: +7 (499) 955-47-00.
E-mail: info@gurucontext.ru.