Самые критичные уязвимости php за время его жизни
Сегодня, когда снова поднимается волна хайпа вокруг очередной уязвимости, я хочу оглянуться назад и вспомнить те дыры в PHP и вокруг него, которые нанесли реальный ущерб тысячам проектов.
Эта статья — не злорадство и не попытка напугать, а скорее попытка систематизировать опыт, который мы, разработчики, получили за два десятилетия.
Почему PHP так часто оказывался в центре внимания
PHP исторически был языком, на котором писали быстро и много. Огромная популярность среди новичков, обилие готовых скриптов, низкий порог входа — всё это привело к тому, что миллионы сайтов работали на коде, который никто не проверял на безопасность. Сам язык тоже не стоял на месте: многие функции появлялись в спешке и позже были признаны опасными. В результате уязвимости в PHP и типовых приложениях на нём стали обычным делом.
Важно понимать: проблема не только в ядре языка, но и в том, как его использовали. Многие катастрофы случались из-за небрежности разработчиков, но были и уязвимости, которые напрямую касались стандартной библиотеки или конфигурации по умолчанию. Ниже я собрал самые заметные и разрушительные случаи за последние двадцать лет.
Регистрация глобальных переменных (register_globals)
В начале 2000-х годов настройка register_globals была включена по умолчанию. Она автоматически превращала параметры запроса в глобальные переменные. Это означало, что любой, кто передавал в URL строку вида ?is_admin=1, мог переопределить переменную $is_admin в скрипте. Многие приложения того времени проверяли права доступа именно через такие флаги, и защита рушилась одним запросом.
Уязвимость была настолько распространённой, что стала символом эпохи. В 2002 году настройку признали опасной и постепенно начали отключать по умолчанию, а в PHP 5.4.0 (2012 год) она была удалена полностью. Тем не менее наследие register_globals до сих пор встречается в старых проектах, которые годами не обновлялись.
Magic Quotes и попытка автоматического экранирования
Ещё одна попытка решить проблему безопасности на уровне языка — magic_quotes_gpc. Эта директива автоматически экранировала кавычки в данных, приходящих из запросов. Задумка была благая: защитить разработчиков от SQL-инъекций. Но на практике она создавала иллюзию защиты и порождала ошибки при работе с базами данных, где требовалось двойное экранирование.
Magic Quotes была удалена в PHP 5.4.0 вместе с register_globals. Многие разработчики пострадали от того, что полагались на это автоматическое экранирование и не использовали подготовленные выражения. Когда настройку убрали, старый код оказался уязвим. Этот случай хорошо иллюстрирует, почему безопасность нельзя перекладывать только на язык.
Удалённое включение файлов (RFI) и LFI
Функции include и require — основа модульности в PHP. Но если имя файла бралось из пользовательского ввода без проверки, злоумышленник мог подключить удалённый скрипт. Это называлось Remote File Inclusion (RFI). В начале 2000-х огромное количество сайтов с параметром ?page= в URL стали жертвами массовых атак. Атакующие подставляли URL своего вредоносного скрипта, и сервер выполнял его.
Local File Inclusion (LFI) позволял читать локальные файлы, например /etc/passwd, или выполнять код через загрузку файлов. Многие известные CMS и форумы (phpBB, WordPress, Joomla) страдали от подобных уязвимостей в своих плагинах и темах. Даже сегодня LFI остаётся распространённой проблемой, хотя современные фреймворки стараются минимизировать риск.
Год | Уязвимость | Тип | Влияние |
|---|---|---|---|
2000 | register_globals | Небезопасная конфигурация | Обход аутентификации, выполнение кода |
2002 | magic_quotes_gpc | Небезопасная конфигурация | Ложное чувство защиты, SQL-инъекции |
2004 | RFI в phpBB | Удалённое включение файлов | Массовое заражение сайтов |
2007 | LFI в WordPress | Локальное включение файлов | Чтение файлов, выполнение кода |
2012 | Удаление register_globals | Изменение языка | Поломка старых приложений |
2019 | CVE-2019-11043 | Переполнение буфера в FPM | Удалённое выполнение кода |

SQL-инъекции: вечная классика
SQL-инъекции не являются уязвимостью самого PHP, но именно на PHP-проектах они получили колоссальное распространение. Причина проста: долгое время не было удобного способа безопасно передавать параметры в запрос. Функция mysql_query не поддерживала подготовленные выражения, а экранирование через mysql_real_escape_string применялось далеко не всегда.
Массовые взломы сайтов через SQL-инъекции привели к появлению целых ботнетов, которые сканировали интернет в поисках уязвимых страниц. Вспомнить хотя бы эпидемию Asprox или массовые атаки на Joomla в 2015 году. Только с приходом PDO и mysqli с поддержкой prepared statements ситуация начала меняться, но и сегодня инъекции остаются в топе OWASP.
Сериализация и десериализация: скрытая угроза
Функции serialize и unserialize в PHP — мощный инструмент, но их использование с пользовательскими данными может привести к уязвимостям типа PHP Object Injection. Если приложение десериализует данные из cookie или параметра запроса, злоумышленник может подставить специально сформированную строку, которая создаст объект с неожиданными свойствами и вызовет цепочку магических методов, приводящих к выполнению кода или чтению файлов.
Один из самых громких случаев — уязвимость в WordPress 2016 года (CVE-2016-10033), связанная с PHPMailer, где десериализация не была основной причиной, но цепочки POP-объектов в популярных библиотеках встречались регулярно. В 2020-х годах исследователи находили цепочки в Laravel, Symfony и других фреймворках. Разработчикам рекомендуется избегать unserialize для непроверенных данных и использовать json_decode.
Уязвимость | Год | Компонент | Последствия |
|---|---|---|---|
CVE-2016-10033 | 2016 | PHPMailer | Удалённое выполнение кода через инъекцию в mail() |
CVE-2018-5712 | 2018 | PHP GD | Чтение памяти, возможное выполнение кода |
CVE-2019-11043 | 2019 | PHP-FPM | Выполнение кода при определённой конфигурации nginx |
CVE-2024-4577 | 2024 | PHP на Windows | Удалённое выполнение кода через инъекцию аргументов |

Уязвимости в самом ядре PHP
Хотя большинство проблем связано с приложениями, в самом PHP тоже находили серьёзные дыры. Например, CVE-2019-11043 в PHP-FPM позволяла выполнить код на сервере при использовании nginx с определённой конфигурацией. Эта уязвимость получила название PHP-FPM underflow и затронула множество высоконагруженных сайтов. Исправление вышло быстро, но новость наделала шума.
Другой пример — CVE-2015-0235, известная как GHOST, затронула функцию gethostbyname в glibc, но PHP-приложения, использующие эту функцию, также оказались под ударом. В 2024 году обнаружили CVE-2024-4577 — уязвимость в PHP на Windows, связанная с передачей аргументов командной строки. Она напомнила, что даже зрелый язык может преподносить сюрпризы.
Проблема с обработкой загрузки файлов
Загрузка файлов — ещё одна область, где PHP-приложения часто ошибались. Проверка MIME-типа через $_FILES['type'] ненадёжна, потому что этот параметр приходит от клиента. Если приложение доверяет ему и сохраняет файл с расширением .php, злоумышленник может загрузить веб-шелл и получить контроль над сервером. Такие случаи массово происходили в 2010-х годах с популярными CMS и форумами.
Даже сегодня многие забывают отключать выполнение PHP в директориях для загрузок. В конфигурации Apache это делается через php_admin_flag engine off или через .htaccess. В nginx — через location с обработкой только статических файлов. Казалось бы, простое правило, но оно спасло бы тысячи сайтов.
Type juggling и магические хеши
PHP — язык с динамической типизацией, и это порождает неочевидные уязвимости. Сравнение строк с помощью оператора == может привести к неожиданным результатам. Например, выражение '0e12345' == '0e67890' возвращает true, потому что обе строки интерпретируются как числа 0. Это свойство использовали для обхода проверок хешей паролей, если хеш начинался с '0e' и содержал только цифры.
В 2014 году эта проблема стала известна как magic hashes. Многие приложения хранили хеши MD5 и сравнивали их через ==. Злоумышленник мог подобрать пароль, MD5-хеш которого начинался с '0e' и далее шли только цифры, и войти в систему без знания реального пароля. Решение — всегда использовать === или, что правильнее, password_verify().
Инъекции в mail() и заголовки
Функция mail() в PHP позволяет передавать дополнительные заголовки. Если в них попадают пользовательские данные без фильтрации, злоумышленник может добавить собственные заголовки, например Bcc или Content-Type, и использовать сервер для рассылки спама. Эта уязвимость была особенно популярна в контактных формах. В 2016 году похожая проблема в PHPMailer (CVE-2016-10033) привела к удалённому выполнению кода, потому что аргументы передавались в системную команду без должной очистки.
Сегодня рекомендуется использовать проверенные библиотеки для отправки почты, такие как PHPMailer или Symfony Mailer, и всегда обновлять их. Также стоит отключить передачу пользовательских данных в пятый аргумент mail().
Влияние на индустрию и уроки
Все эти уязвимости научили нас нескольким важным вещам. Во-первых, безопасность не может быть «функцией по умолчанию» — её нужно проектировать. Во-вторых, сообщество PHP сделало огромный шаг вперёд: появились PSR-стандарты, фреймворки с встроенной защитой, инструменты статического анализа. В-третьих, обновление PHP — это не прихоть, а необходимость. Многие из описанных проблем исчезли в новых версиях, но старый код продолжает работать на устаревших серверах.
Я часто вижу проекты, которые годами сидят на PHP 5.6 или даже 5.3. Их владельцы считают, что «работает — не трогай». Но именно такие сайты становятся лёгкой добычей. Хайп вокруг новых уязвимостей — хороший повод провести аудит и обновить зависимости.
Современные методы защиты
Сегодня у разработчиков на PHP есть множество инструментов для предотвращения уязвимостей. Подготовленные выражения в PDO и mysqli, экранирование вывода через htmlspecialchars, использование современных фреймворков (Laravel, Symfony), которые берут на себя рутинные проверки. Также важно следить за обновлениями и использовать инструменты вроде PHPStan, Psalm, Composer audit.
Всегда используйте подготовленные выражения для запросов к базе данных.
Проверяйте и фильтруйте весь пользовательский ввод, но не полагайтесь только на фильтрацию.
Экранируйте вывод в HTML через htmlspecialchars с флагами ENT_QUOTES и UTF-8.
Отключайте выполнение PHP в директориях для загрузок.
Обновляйте PHP и библиотеки, следите за CVE.
Используйте строгое сравнение === там, где это важно.
Не используйте unserialize для данных из ненадёжных источников.
Эти правила кажутся простыми, но их нарушение до сих пор приводит к инцидентам. Я сам видел, как проект с миллионной аудиторией использовал mysql_query с конкатенацией строк — и это было в 2020 году.
Запросить аудит безопасности
Заключение
За двадцать лет PHP прошёл путь от опасного новичка до зрелого языка с развитой экосистемой. Уязвимости были и будут, но их количество и влияние снижаются благодаря усилиям сообщества. Хайп вокруг очередной дыры — это не повод отказываться от языка, а возможность проверить свои проекты и сделать их надёжнее. Безопасность — это процесс, а не состояние. И я рад, что за пятнадцать лет моей практики подход к ней изменился к лучшему.