NextPDF Gotenberg: безопасность и эксплуатация
Краткий обзор
Заголовок раздела «Краткий обзор»Этот мост отправляет документы из вашего приложения во внешнюю сетевую службу. Поэтому он одновременно является поверхностью запросов на стороне сервера и поверхностью транспортной безопасности. Пакет реализует конкретные проверяемые средства защиты для обеих поверхностей. Сам по себе он не защищает всю систему. Эти средства работают только при развёртывании и эксплуатации Gotenberg с соответствующими мерами предосторожности. На этой странице описано, какие средства защиты реализует пакет и какие эксплуатационные обязанности их дополняют.
Ничто здесь не является гарантией. Каждое средство защиты — это конкретное поведение, покрытое тестами, с явно указанными ограничениями.
Два уровня политики
Заголовок раздела «Два уровня политики»Мост применяет две разные политики безопасности на разных уровнях:
- Транспортная политика (
GotenbergSecurityPolicy) — обязательная проверка схемы URL, проверка на подделку запросов на стороне сервера (SSRF), защита от перепривязки системы доменных имён (DNS), ограничения размера входных данных и проверка имён файлов. Ниже подробно описан именно этот уровень. - Политика разбора HTML — это политика содержимого на уровне разбора, которая по умолчанию использует стандартную политику ядра NextPDF и срабатывает до того, как содержимое достигнет средства отрисовки. Она дополняет транспортную политику, но остаётся независимой от неё. Эта страница посвящена транспортной политике.
Проверка на подделку запросов на стороне сервера (SSRF)
Заголовок раздела «Проверка на подделку запросов на стороне сервера (SSRF)»Мост проверяет настроенный URL API до того, как из процесса выйдет хотя бы один байт. Это средство защиты состоит из трёх частей.
Принудительная проверка схемы. Принимается только https (без учёта регистра). URL вида http:// отклоняется. Поэтому Transport Layer Security (TLS) обязателен для каждого преобразования и каждой проверки работоспособности.
Проверка адреса. Если узел задан IP-литералом, мост отклоняет его, когда он попадает в частный или зарезервированный диапазон адресов. Если узел задан именем, мост разрешает его до всех записей A и AAAA и отклоняет запрос, если любой из разрешённых адресов является частным или зарезервированным. Разрешение полного набора записей, а не одного адреса, нейтрализует сценарий, когда злоумышленник прячет частный адрес за именем, которое также возвращает публичный адрес. Это соответствует подходу из руководства OWASP по предотвращению SSRF: получить все IP-адреса, связанные с доменным именем (записи A и AAAA, для IPv4 и IPv6), и проверить каждый из них по списку разрешённых (OWASP Cheat Sheet Series, предотвращение SSRF, защита на уровне приложения; зафиксировано в RAG-сайдкаре страницы (retrieval-augmented generation)).
Повторная проверка «время проверки/время использования» (TOCTOU). Мост повторно разрешает имя и сравнивает набор адресов, зафиксированный во время проверки, непосредственно перед отправкой запроса. Если появляется новый адрес, запрос прерывается с ошибкой перепривязки DNS. Это закрывает промежуток между проверкой и подключением, который иначе могла бы использовать атака с перепривязкой.
Когда мост использует свой транспорт с закреплением на базе cURL, проверенный набор адресов привязывается к соединению с помощью CURLOPT_RESOLVE, поэтому подключение идёт к проверенному адресу, а не к адресу, который может вернуть новый DNS-запрос во время подключения. Переход по переадресациям на этом транспорте отключён (CURLOPT_FOLLOWLOCATION выключено, CURLOPT_MAXREDIRS равно нулю), поэтому ответ 3xx не может незаметно отправить запрос на непроверенный узел. Вместо этого ответ передаётся уровню политики.
Эксплуатационное следствие. Защита от SSRF по своей конструкции отклоняет частные и зарезервированные адреса. Если ваш Gotenberg работает в частной сети, вы не можете направить мост на его частный адрес. Откройте к нему доступ через адрес, который допускает защита, и защитите этот путь сегментацией сети и аутентификацией, как описано в разделе о развёртывании ниже.
Транспортная безопасность и закрепление открытого ключа
Заголовок раздела «Транспортная безопасность и закрепление открытого ключа»В транспорте с закреплением на базе cURL всегда включены проверка TLS-пира и имени хоста (CURLOPT_SSL_VERIFYPEER равно true, CURLOPT_SSL_VERIFYHOST равно 2). Помимо стандартной проверки цепочки, мост поддерживает закрепление SubjectPublicKeyInfo (SPKI).
Каждый закреплённый ключ — это хеш SHA-256 структуры SubjectPublicKeyInfo сервера, выраженный как sha256/<base64>. Мост принимает сертификат, когда его хеш SPKI совпадает с любым закреплённым ключом из объединённого набора основных и резервных ключей. Эта модель резервного закрепления соответствует RFC 7469 §4.3: резервный закреплённый ключ — отпечаток вторичной, ещё не развёрнутой пары ключей — рассматривается как основной способ восстановления после непреднамеренного сбоя проверки закрепления; она также соответствует §2.5, где требуется, чтобы закреплённый набор включал хотя бы один ключ, отсутствующий в текущей цепочке сертификатов (RFC 7469 §4.3 и §2.5; зафиксировано в RAG-сайдкаре страницы). Код моста ссылается на RFC 7469 §2.1 и §2.6 для семантики «хотя бы один резервный закреплённый ключ» и пересечения объединённого набора. Закрепление включается опционально: если закреплённые ключи не настроены, применяется стандартная проверка цепочки, а закрепление не действует.
Закреплённый ключ, который не удаётся разобрать, вызывает ошибку конфигурации до любого запроса. Действительный сертификат, чей SPKI не совпадает ни с одним настроенным закреплённым ключом, приводит к отклонению запроса транспортом — это предусмотрено конструкцией.
Процедура ротации закреплённых ключей
Заголовок раздела «Процедура ротации закреплённых ключей»Неправильная ротация блокирует доступ моста к службе. Выполняйте ротацию без простоя:
- Перед сменой ключа сервера сгенерируйте закреплённый ключ SPKI для нового ключа и добавьте его в список резервных закреплённых ключей. Разверните эту конфигурацию. Теперь мост принимает как текущий, так и будущий ключ.
- Переключите сертификат или ключ сервера на использование нового ключа.
- Убедитесь, что преобразования по-прежнему выполняются успешно (новый ключ теперь совпадает с резервным закреплённым ключом).
- Переместите новый закреплённый ключ из списка резервных в основной список и удалите закреплённый ключ для ключа, выведенного из эксплуатации. Разверните.
- Сгенерируйте и подготовьте закреплённый ключ для следующей ротации в качестве нового резервного, чтобы в наборе всегда был пригодный запасной.
Хранение списка резервных отдельно от основного списка позволяет подготовить и проверить следующий закреплённый ключ, не затрагивая активный.
Аутентификация
Заголовок раздела «Аутентификация»Если apiKey не пустой, мост отправляет его в заголовке Authorization: Bearer с запросом на преобразование. Поле помечено #[\SensitiveParameter], поэтому его значение скрывается из трассировок стека. Мост не загружает секрет за вас; передавайте его из менеджера секретов при запуске процесса и никогда не фиксируйте в репозитории. В журнал запросов токен не попадает: записанная отладочная строка содержит только URL, имя файла, формат и длину содержимого.
Граница доверия для ответа
Заголовок раздела «Граница доверия для ответа»Ответ принимается только тогда, когда статус равен 200, Content-Type содержит application/pdf, а тело начинается с сигнатуры %PDF. Проверка байтовой сигнатуры важна, потому что один лишь заявленный тип содержимого не доказывает, что представляют собой байты. Стандарт WHATWG MIME Sniffing формализует тот же подход в своём алгоритме определения MIME-типа: вычисленный тип выводится из сопоставления начальных байтовых шаблонов, а не из указанного типа. Руководство OWASP по загрузке файлов формулирует соответствующий принцип на уровне приложения: проверяйте тип файла и не доверяйте заявленному заголовку Content-Type, поскольку его можно подделать (WHATWG MIME Sniffing §6.2.3; OWASP Cheat Sheet Series, проверка загрузки файлов; оба источника зафиксированы в RAG-сайдкаре страницы). Мост применяет эквивалентную защитную проверку на входе: несовпадение вызывает типизированное исключение, а байты никогда не возвращаются в качестве результата.
Именно поэтому здесь также важен контракт PSR-18. Клиент PSR-18 выбрасывает исключение только тогда, когда не может отправить запрос или не может разобрать ответ в объект PSR-7, — он не выбрасывает исключение при статусе ошибки. Корректно сформированный ответ 4xx/5xx возвращается вызывающей стороне обычным образом (PSR-18, “Exceptions”; зафиксировано в RAG-сайдкаре страницы). Поэтому мост сам проверяет статус, тип и сигнатуру, а не предполагает, что возвращённый ответ успешен. HTTP-семантика нарушения ограничения content-type — отклонение 415 (Unsupported Media Type), когда сервер отклоняет содержимое в неподдерживаемом формате; эту модель отражает входящая проверка (RFC 9110 §15.5.16; зафиксировано в RAG-сайдкаре страницы).
Ограничения ресурсов
Заголовок раздела «Ограничения ресурсов»У моста есть одно внутрипроцессное ограничение ресурсов: maxFileSize (по умолчанию 52,428,800 байт = 50 MiB). Оно применяется до запроса, поэтому слишком большие входные данные никогда не достигают службы. В мосте нет встроенного ограничения параллелизма, ограничения частоты запросов или ограничения размера выходных данных. Это обязанности развёртывания и вызывающей стороны (см. /integrations/gotenberg/production-usage/). Установите maxFileSize на наименьшее значение, которое требуется вашим реальным документам: более жёсткое ограничение дешевле как средство защиты от отказа в обслуживании.
Развёртывание и защита службы Gotenberg
Заголовок раздела «Развёртывание и защита службы Gotenberg»Мост безопасен ровно настолько, насколько безопасна служба, которую он вызывает. Эту службу эксплуатируете вы; перечисленные ниже обязанности дополняют описанные выше средства защиты.
- Завершайте TLS перед Gotenberg. Контейнер Gotenberg по умолчанию использует обычный HTTP. Мост требует HTTPS, поэтому разместите Gotenberg за обратным прокси, ingress-контроллером или сервисной сеткой, которые завершают TLS, и направьте мост на конечную точку HTTPS. Закрепите SPKI прокси, если вы включаете закрепление.
- Не открывайте Gotenberg публично. Он выполняет преобразование документов с помощью движков класса LibreOffice и Chromium и не является службой, предназначенной для прямого доступа из интернета. Разрешите входящий трафик только от хостов приложения, которые его вызывают, с помощью сетевой политики или правил межсетевого экрана.
- Требуйте аутентификацию на этом пути. Мост отправляет bearer-токен, если он настроен; обеспечьте его проверку (или взаимный TLS) на прокси, чтобы неаутентифицированный запрос не мог достичь движка преобразования.
- Закрепите конкретную версию службы. Мост рассчитывает ровно на два пути службы —
/forms/libreoffice/convertи/health. Закрепите образ Gotenberg на конкретном патч-теге, проверьте эти два пути для развёртываемой версии и повторяйте проверку при каждом обновлении. - Осознанно рассчитывайте пропускную способность преобразования. Каждое преобразование удерживает обработчик на время выполнения запроса. Проектируйте развёртывание Gotenberg под пиковую частоту одновременных преобразований и соответственно ограничьте число одновременно выполняемых преобразований у вызывающей стороны. Пропускная способность — это свойство вашего развёртывания, а не этого пакета.
- Считайте входные данные преобразования недоверенными. Документы, проходящие через преобразование, обрабатываются сложными движками. Ограничьте
maxFileSize, изолируйте развёртывание Gotenberg (отдельный сетевой сегмент, минимальный исходящий трафик, отсутствие доступа к внутренним службам) и поддерживайте движок в актуальном состоянии.
Чего этот пакет не утверждает
Заголовок раздела «Чего этот пакет не утверждает»- Он не является «безопасным по умолчанию»: средства защиты реальны, но зависят от правильного развёртывания и настройки.
- Он не делает преобразование «защищённым от изменения», а результат — «сертифицированным». Он проверяет транспорт и структуру ответа; он не заверяет содержимое документа.
- Он не обеспечивает подписание, проставление меток времени или долгосрочную проверку. Это вопросы постобработки. Поддержка PAdES в редакции Pro ограничена только базовым уровнем B-B и не включает B-T, B-LT или B-LTA; ничто в этом мосте не подразумевает возможности проставления меток времени или LTV.
- Он не поддерживает «все файлы Office». Он поддерживает шесть перечисленных форматов и отклоняет всё остальное до любого запроса.
См. также
Заголовок раздела «См. также»- /integrations/gotenberg/configuration/ — правила выбора транспорта и полная модель закрепления.
- /integrations/gotenberg/production-usage/ — повторные попытки, тайм-ауты, параллелизм и наблюдаемость.
- /integrations/gotenberg/troubleshooting/ — каждое исключение безопасности и его причина.
- /integrations/gotenberg/overview/ — процесс преобразования и модель зависимостей.