Недавно обнаруженная критическая уязвимость в популярной библиотеке PHP Data Objects (PDO) позволяет злоумышленникам выполнять SQL-инъекции даже в случаях, когда разработчики корректно используют подготовленные выражения. Проблема затрагивает миллионы веб-приложений по всему миру, поскольку PDO является одним из основных инструментов для работы с базами данных в PHP. Уязвимость была выявлена в ходе анализа задания на соревновании по информационной безопасности DownUnderCTF, и её эксплуатация основана на особенностях работы SQL-парсера PDO.
Главная причина уязвимости кроется в том, что PDO по умолчанию использует эмуляцию подготовленных запросов вместо нативных подготовленных выражений, предоставляемых СУБД. Вместо того чтобы передавать параметры напрямую в базу данных, PDO обрабатывает их собственным парсером, который может некорректно интерпретировать пользовательский ввод в определённых контекстах. В частности, ошибки возникают, когда входные данные подставляются непосредственно в SQL-запрос в местах, где параметры не могут быть привязаны стандартными средствами PDO, например в именах таблиц и столбцов.
Для демонстрации проблемы исследователи приводят пример кода, который на первый взгляд кажется безопасным:
1 2 3 4 5 6 | $dsn = "mysql:host=127.0.0.1;dbname=demo"; $pdo = new PDO($dsn, 'root', ''); $col = '`' . str_replace('`', '``', $_GET['col']) . '`'; $stmt = $pdo->prepare("SELECT $col FROM fruit WHERE name = ?"); $stmt->execute([$_GET['name']]); $data = $stmt->fetchAll(PDO::FETCH_ASSOC); |
Несмотря на попытку экранировать ввод пользователя, злоумышленники могут обойти защиту, используя специально сформированные строки, содержащие нулевые байты и символы вопроса. Например, если передать в параметр "col" значение "?#\0", парсер PDO может ошибочно распознать вопросительный знак как место для подстановки параметра, что открывает путь к SQL-инъекциям.
Один из возможных векторов атаки включает манипуляцию логикой парсера через сложные конструкции, такие как:
1 | http://localhost:8000/?name=x FROM (SELECT table_name AS 'x from information_schema.tables)y;#&col=?#%00" |
Такой запрос позволяет извлекать метаданные базы данных, включая имена таблиц, что может быть использовано для дальнейших атак.
Степень уязвимости зависит от используемой СУБД. Например, MySQL подвержен атакам по умолчанию, если разработчики не отключают эмуляцию подготовленных запросов через "PDO::ATTR_EMULATE_PREPARES". PostgreSQL становится уязвимым только при включённой эмуляции, а SQLite остаётся защищённым благодаря особенностям обработки нулевых байтов.
Особую опасность представляют старые версии PHP (8.3 и ранее), в которых использовался единый SQL-парсер для всех баз данных, что повышало вероятность успешной эксплуатации уязвимости.
Для защиты эксперты рекомендуют разработчикам:
- Отключать эмуляцию подготовленных запросов через "PDO::ATTR_EMULATE_PREPARES" там, где это возможно.
- Обновлять PHP до актуальных версий (8.4 и новее), где учтены многие проблемы парсера.
- Запрещать использование нулевых байтов в SQL-запросах.
- Избегать смешивания ручного формирования SQL-фрагментов с привязкой параметров через PDO, особенно при включённой эмуляции.
Этот случай ещё раз подчёркивает важность тщательного тестирования безопасности даже в тех ситуациях, когда код кажется защищённым. Разработчикам не стоит полагаться исключительно на стандартные механизмы защиты, такие как подготовленные выражения, а также необходимо учитывать особенности конкретных библиотек и версий программного обеспечения.
Факт обнаружения такой уязвимости в широко используемом инструменте, как PDO, служит напоминанием о том, что даже проверенные временем решения могут содержать скрытые уязвимости, которые могут быть использованы злоумышленниками для компрометации систем. Поэтому постоянный аудит кода, своевременное обновление зависимостей и глубокое понимание внутренней работы используемых библиотек остаются критически важными аспектами обеспечения безопасности веб-приложений.