Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.

Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.

Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.

Спонсор
Модель безопасности

Модель безопасности

Этот документ описывает модель доверия FrankenPHP: какие входные данные являются доверенными, какие нет, и где проходит граница между ними. Он предназначен для помощи аудитам безопасности и автоматизированным сканерам в анализе самого FrankenPHP, а не PHP-приложений, которые он обслуживает.

Внутренние механизмы, упомянутые здесь (потоки, граница CGO, изоляция среды), см. в Внутреннее устройство. Сохранение состояния в долгоживущих процессах см. в Режим воркера.

# Границы доверия

FrankenPHP работает в стеке с четырьмя различными участниками:

УчастникДовериеПримечания
Удаленный клиентНедоверенныйHTTP-запрос (метод, URI, заголовки, куки, тело, загрузки) является основным источником загрязненных входных данных.
ОператорДоверенныйПредоставляет конфигурацию развертывания: Caddyfile, переменные среды, php.ini, установленные расширения PHP и модули Caddy, а также сам код приложения.
Код PHP-приложенияДоверенный по происхождениюРазвертывается оператором, поэтому FrankenPHP никогда не выполняет код, предоставленный злоумышленником, но этот код потребляет недоверенные данные запроса.
FrankenPHP (Go + C)Доверенная вычислительная базаВстраивает PHP, передает данные внутрь и наружу, а также изолирует запросы и потоки. Собственные дефекты FrankenPHP являются предметом рассмотрения этого документа.

# Происхождение кода против загрязнения данных

Это самое важное различие, которое разрешает большую часть путаницы “доверяем ли мы PHP или нет?”:

  • Происхождение кода доверено. FrankenPHP выполняет только PHP-файлы, развернутые оператором: скрипт, разрешенный в корневом каталоге документа, или настроенный скрипт воркера. Он никогда не выполняет код, переданный в самом запросе (тело, строка запроса, заголовки): граница SAPI переносит данные, но никогда недоверенный код.
  • Данные запроса загрязнены. Все, что PHP-код считывает из запроса ($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER, php://input), является недоверенным, точно так же, как в любом PHP SAPI. Санитаризация данных — задача приложения.

Таким образом, “мы доверяем тому, что поступает от PHP” верно для кода, в то время как “SAPI переносит недоверенный ввод” верно для данных: эти два утверждения не противоречат друг другу. Задача FrankenPHP — добросовестно переносить эти загрязненные данные и предотвращать утечку данных одного запроса в другой.

# За что отвечает FrankenPHP

Доверенная вычислительная база выполняет три задачи. Дефекты безопасности в FrankenPHP находятся в одной из них:

  1. Надежная передача: сопоставление запроса с суперглобальными переменными PHP и php://input, а также передача вывода и заголовков PHP обратно клиенту, без внедрения инъекций (инъекция заголовка/CRLF, подмена запросов, выполнение неправильного файла).
  2. Изоляция: предотвращение пересечения состояния, ограниченного запросом, между запросами, между потоками PHP и между итерациями воркера.
  3. Безопасность памяти: управление границей CGO (Go ↔ C/PHP) без повреждения памяти.

# В области рассмотрения: собственная поверхность атаки FrankenPHP

Это поверхности, которыми владеет FrankenPHP. Уязвимость здесь является уязвимостью FrankenPHP:

  • Сопоставление запроса с суперглобальными переменными (cgi.go, frankenphp_register_server_vars): создание $_SERVER, REMOTE_ADDR, SCRIPT_NAME, PATH_INFO и других CGI-переменных из запроса.
  • Разрешение пути PHP-скрипта: путь запроса делится по split_path (по умолчанию .php) на SCRIPT_NAME / PATH_INFO, затем соединяется с корневым каталогом документа с помощью sanitizedPathJoin (filepath.Join(root, filepath.Clean("/"+reqPath))), что предотвращает выход SCRIPT_FILENAME за пределы корневого каталога документа (обход каталогов). Директива php_server дополнительно устанавливает перезапись try_files по умолчанию, которая маршрутизирует запросы к существующим файлам или фронт-контроллеру, уменьшая классическую проблему PHP-FPM, связанную с выполнением неправильного файла.
  • Изоляция состояния в режиме воркера: FrankenPHP сбрасывает $_GET, $_POST, $_COOKIE, $_FILES, $_SERVER и $_REQUEST между запросами, явно очищает $_SESSION (которая в противном случае будет утекать между запросами) и ошибки, возвращаемые error_get_last() и libxml_get_errors(), а также заголовки, возвращаемые http_get_last_response_headers(), состояние strtok() и переменные, добавленные output_add_rewrite_var(), но $_ENV не сбрасывается, а записи putenv(), static переменные, статические свойства классов, глобальные переменные и настройки движка времени выполнения (ini_set(), контекст потока по умолчанию, часовой пояс по умолчанию, рабочий каталог, …) сохраняются между запросами в одном и том же потоке. Данные, относящиеся к запросу или пользователю, оставленные в этом состоянии, могут утечь в последующий запрос (см. Режим воркера).
  • Изоляция среды для каждого потока: frankenphp_putenv() / frankenphp_getenv() работают с потоко-локальной sandboxed_env. Записи putenv() остаются внутри этой песочницы и никогда не достигают глобальной среды ОС процесса, поэтому секрет, установленный одним запросом, не может утечь к дочерним процессам (proc_open, exec, …), на сторону Go/Caddy или к другим запросам. Дочернему процессу, который должен видеть такую переменную, она должна быть явно передана через аргумент среды proc_open() или exec() (см. Внутреннее устройство).
  • Граница памяти CGO: фиксация строк Go и время жизни C.CString() / free() на границе Go ↔ C.
  • Caddy admin API: конечные точки /frankenphp/workers/restart и /frankenphp/threads, предоставляемые через Caddy’s admin API (который по умолчанию прослушивает localhost:2019). Предоставление этой конечной точки за пределы localhost является решением оператора.
  • Обработка доверенного прокси: входящие заголовки X-Forwarded-* всегда достигают PHP как загрязненные значения $_SERVER['HTTP_X_FORWARDED_*']; они считаются доверенными для определения реального IP-адреса клиента и схемы только при настроенном trusted_proxies.
  • Медленные тела запросов: клиент, который объявляет тело, а затем передает его по каплям или задерживает, удерживает обрабатывающий поток на все время. При ограниченном пуле потоков достаточное количество таких соединений истощает его (DoS-атака типа “медленный POST”). FrankenPHP применяет таймаут простоя в 60 секунд при чтении тела по умолчанию (request_body_timeout), сбрасывая крайний срок перед каждым чтением, так что стабильная загрузка любого размера проходит успешно, в то время как застопорившаяся обрезается, а поток освобождается.

# Вне области рассмотрения

  • Уязвимости в PHP-коде приложения (SQL-инъекции, XSS, небезопасная десериализация и т. д.). FrankenPHP доставляет недоверенные данные запроса приложению без изменений; защита от них — ответственность приложения, как и в случае с любым SAPI.
  • Недостатки в сторонних компонентах, используемых FrankenPHP (PHP, Caddy, Go) или в проектах, построенных на его основе (Laravel Octane, Symfony Runtime). Сообщайте о них соответствующему проекту.

# Сообщение об уязвимости

См. документ SECURITY.md для получения информации о том, как сообщить о проблеме безопасности, затрагивающей FrankenPHP.

Редактировать эту страницу