Безопасность и эксплуатация Artisan
Мост отрисовывает в Chrome HTML, который может быть недоверенным, помещая его за двумя независимыми сетевыми барьерами и строгой политикой содержимого. Песочница Chrome на уровне операционной системы — отдельный, необязательный механизм контроля с явными ограничениями. На этой странице описана граница защиты. Здесь не утверждается, что эта граница абсолютна.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Отрисовка — это выполнение запроса на стороне сервера: ваше приложение передаёт HTML движку браузера, который по умолчанию может загружать ресурсы. Когда недоверенные входные данные управляют исходящей загрузкой, возникает риск подделки запроса на стороне сервера (SSRF): запись CWE-918 в каталоге Common Weakness Enumeration (CWE) определяет её как извлечение сервером содержимого переданного URL без достаточной уверенности в том, что запрос достигнет ожидаемого назначения. SSRF (CWE-918) входит в перечень CWE Top 25. Стандарт проверки безопасности приложений (ASVS) проекта Open Worldwide Application Security Project (OWASP) требует контролировать исходящие запросы серверных компонентов, а не оставлять их неявными. Памятка OWASP SSRF Prevention Cheat Sheet рассматривает запрет вызовов к произвольным назначениям на сетевом уровне как надёжный механизм контроля. Описанная ниже сетевая модель “запрет по умолчанию” — ответ моста на это требование. Документ National Institute of Standards and Technology (NIST) Special Publication (SP) 800-53 SC-7 описывает тот же принцип границы: “запретить всё, разрешать в порядке исключения”; мост применяет его на транспортном уровне.
Резидентность данных и меры защиты ПДн
Заголовок раздела «Резидентность данных и меры защиты ПДн»HTML, передаваемый мосту, полностью обрабатывается в рамках процесса и внутри локального экземпляра Chrome. Сам мост не выполняет исходящих сетевых вызовов и не позволяет Chrome выполнять их (см. сетевую модель ниже), поэтому содержимое входных данных не покидает узел через средство отрисовки. Персональные данные (ПДн) из входных данных попадают в создаваемые вами выходные данные формата Portable Document Format (PDF), поэтому применяйте к выходным данным те же средства контроля резидентности, что и к входным. Мост не сохраняет входные или выходные данные на диск; за сохранение отвечает вызывающая сторона.
Безопасная телеметрия и очистка журналов
Заголовок раздела «Безопасная телеметрия и очистка журналов»ChromeHtmlRenderer и BrowserPool принимают необязательный LoggerInterface по стандарту PHP Standard Recommendation (PSR)-3. Мост записывает в журнал только эксплуатационные метаданные: длину входных данных в байтах, целевую ширину и высоту, длину выходных данных в байтах, измеренную высоту содержимого, запуск браузера с настроенным путём к исполняемому файлу, уведомления о перезапуске со счётчиком отрисовок и события закрытия. Он не записывает в журнал содержимое HTML, отрисованные байты или извлечённый текст. Это согласуется с рекомендациями NIST SP 800-92: вести журнал эксплуатационных событий, не помещая в журналы конфиденциальные полезные нагрузки. Путь к исполняемому файлу попадает в журнал. Считайте его неконфиденциальными метаданными развёртывания. Тесты проверяют форму вызовов журналирования в tests/Unit/Artisan/ChromeHtmlRendererTest.php::renderLogsDebugWithSizeWidthHeightAndPdfSize и tests/Unit/Artisan/BrowserPoolTest.php::getBrowserLogsInfoOnLaunchWithBinaryPath.
Модель сетевой изоляции (эшелонированная защита)
Заголовок раздела «Модель сетевой изоляции (эшелонированная защита)»Мост использует два независимых барьера, чтобы обход одного из них не ставил узел под угрозу:
-
Content-Security-Policy. Каждая отрисовка оборачивается
ChromeSecurityPolicy::wrapHtml()в документ, который несёт:default-src 'none'; style-src 'unsafe-inline'; img-src data:;base-uri 'none'; form-action 'none'; frame-ancestors 'none';navigate-to 'none';Директива политики безопасности содержимого (CSP)
default-src 'none'запрещает все источники ресурсов.img-src data:разрешает только встроенные изображения.navigate-to 'none'блокирует навигацию на стороне клиента.style-src 'unsafe-inline'— единственное послабление, необходимое ChromeprintToPDFдля применения встроенных стилей. Это проверено вsrc/Artisan/ChromeSecurityPolicy.phpи подтверждено тестомChromeSecurityPolicyTest::wrapHtmlIncludesNavigationCspDirectives. -
Блокировка транспорта Chrome DevTools Protocol (CDP). Перед загрузкой содержимого
ChromeHtmlRendererотправляетNetwork.enable, а затемNetwork.setBlockedURLsс шаблоном['*']. Это блокирует каждый URL подресурса на транспортном уровне Chrome DevTools Protocol, независимо от CSP. Это проверено вsrc/Artisan/ChromeHtmlRenderer::blockAllNetworkRequests()и подтверждено тестомChromeHtmlRendererTest::renderAutoFitsHeightAndBlocksNetworkRequests(он проверяет точный порядок методов CDP и параметр['urls' => ['*']]). Такая блокировка на сетевом уровне соответствует механизму контроля, который рекомендации OWASP по SSRF называют сильнейшим; это запрет всего на транспортном уровне, согласующийся с NIST SP 800-53 SC-7.
Результат: удалённый URL в <img>, таблице стилей, шрифте, скрипте или iframe во входных данных не загружается. Мост не реализует список разрешённых доменов или фильтр частных IP-адресов, потому что они ему не нужны: он вообще не допускает исходящую загрузку подресурсов.
Примечание о расхождении: docblock
nextpdf/coreдляwriteHtmlChrome()говорит, что Chrome “will fetch external resources”, и советует настроить политику, чтобы “block private IP ranges and limit allowed domains.” Это описывает настраиваемую модель списка разрешений. Поставляемая в ArtisanChromeSecurityPolicyне предоставляет списка разрешений; она безусловно блокирует все запросы подресурсов. Авторитетен код, а не docblock ядра. Это расхождение зафиксировано для команды документации ядра.
Проверка входных данных (до Chrome)
Заголовок раздела «Проверка входных данных (до Chrome)»ChromeSecurityPolicy::validate() выполняется до того, как мост обратится к Chrome, и отклоняет:
| Проверка | Ограничение | Обоснование |
|---|---|---|
| Размер HTML | > maxHtmlSize (по умолчанию 5 МБ) | Ограничение на исчерпание ресурсов (неконтролируемое потребление ресурсов из CWE Top 25) |
| Data URI в кодировке Base64 | группа захвата >= 13_000_000 байт | Ограничение против “бомб” распаковки |
<meta http-equiv="refresh"> | любой (без учёта регистра, single/double кавычки) | Блокирует перенаправления на стороне клиента к внутренним конечным точкам — вектор навигации SSRF |
Блокировка meta-refresh — явное усиление защиты от SSRF. Без неё подконтрольный злоумышленнику HTML мог бы перенаправить Chrome на конечную точку метаданных облака до printToPDF. Граничное поведение подтверждается в ChromeSecurityPolicyTest (validateThrowsOnOversizedHtml, validateRejectsMetaRefreshRedirect, validateRejectsMetaRefreshCaseInsensitive, validateRejectsMetaRefreshWithSingleQuotes, validateRejectsOversizedBase64DataUri, validateRejectsBase64DataUriAtExactThreshold).
Кроме того, ChromeSecurityPolicy::wrapHtml() удаляет </style> из defaultCss перед внедрением, чтобы не допустить выхода из блока стилей в контекст скрипта (подтверждено тестом ChromeSecurityPolicyTest::wrapHtmlStripsStyleClosingTagsFromDefaultCss).
Граница песочницы Chrome — явное изложение
Заголовок раздела «Граница песочницы Chrome — явное изложение»Песочница Chrome на уровне операционной системы — отдельный механизм контроля по сравнению с описанными выше сетевыми барьерами, и мост её не гарантирует.
- По умолчанию
noSandboxравноfalse, поэтому Chrome запускается со включённой собственной песочницей. Мост не реализует эту песочницу; он полагается на песочницу исполняемого файла Chrome, которая зависит от поддержки ядра узла. - Установка
noSandbox: trueзапускает Chrome с--no-sandbox. Это удаляет песочницу изоляции процессов Chrome. Такой вариант предусмотрен для контейнеров, где песочница не может инициализироваться. Это реальное снижение изоляции: компрометация средства отрисовки больше не сдерживается песочницей Chrome. - Сетевые барьеры моста (CSP + блокировка CDP) остаются в силе независимо от того, включена ли песочница, но они не заменяют изоляцию процессов. Применяются рекомендации OWASP ASVS о наименьших привилегиях: запускайте Chrome от имени пользователя без прав root, в ограниченном контейнере, используйте
noSandboxтолько там, где это неизбежно, и рассматривайте развёртывание с--no-sandboxкак требующее более высокого доверия к входным данным.
В этой документации не утверждается, что мост “secure by default” или “tamper-proof”. В ней также не утверждается, что отключение песочницы безопасно. Здесь описаны имеющиеся механизмы контроля и границы их действия. Подготовка контейнера с поддержкой песочницы описана на странице /integrations/artisan/chrome-renderer-setup/.
Режимы сбоев
Заголовок раздела «Режимы сбоев»Эти режимы сбоев перечислены на основе src/Artisan/Exception/ и кода render/transport:
| Условие | Проявляется как | Источник |
|---|---|---|
chrome-php/chrome отсутствует | ChromeNotAvailableException (с командой установки) | BrowserPool::getBrowser() |
HTML превышает maxHtmlSize | RuntimeException (с сообщением “exceeds maximum allowed size” — превышен максимальный размер) | ChromeSecurityPolicy::validate() |
| Data URI в кодировке Base64 чрезмерного размера | RuntimeException (с сообщением “oversized base64 data URI” — data URI Base64 чрезмерного размера) | ChromeSecurityPolicy::validate() |
| Запрещённый meta-refresh | RuntimeException (с сообщением “forbidden meta refresh redirect” — запрещённое перенаправление meta-refresh) | ChromeSecurityPolicy::validate() |
| Запуск / тайм-аут / сбой Chrome | ChromeRenderException (оборачивая причину) | ChromeHtmlRenderer::render() |
| Chrome вернул пустой PDF | ChromeRenderException (с сообщением “returned empty data” — возвращены пустые данные) | ChromeHtmlRenderer::render() |
| У страницы нет потока содержимого | PdfParseException | PageImporter::import() |
Если ChromeRenderException возникает внутри отрисовки, оно повторно выбрасывается без изменений. Любой другой Throwable оборачивается в ChromeRenderException с сохранением предыдущего исключения (подтверждено тестами ChromeHtmlRendererTest::renderRethrowsChromeRenderExceptionWithoutWrapping и ::renderWrapsUnexpectedThrowablesWithChromeRenderException). Страница Chrome всегда закрывается в блоке finally, даже при сбое.
Ограничения ресурсов
Заголовок раздела «Ограничения ресурсов»- Размер входных данных:
maxHtmlSize(по умолчанию 5 МБ) и ограничение data URI в Base64 в 13 МБ. - Время:
renderTimeoutсекунд ограничивает и загрузку содержимого, и синхронные вызовы CDP. Управляющие команды CDP используют фиксированный тайм-аут 5 секунд. - Процесс:
BrowserPoolперезапускает Chrome каждые 100 отрисовок, чтобы ограничить рост памяти, и закрывает процесс приclose()/ уничтожении.
Это ограничения, а не квоты. Для любого пути, доступного недоверенным входным данным, всё равно используйте ограничения ресурсов на уровне узла (cgroup, ulimit, бюджет запросов), согласующиеся с рекомендациями CWE Top 25 по потреблению ресурсов.
Хуки наблюдаемости
Заголовок раздела «Хуки наблюдаемости»Внедрите логгер PSR-3, чтобы фиксировать начало отрисовки (размер, ширина, высота), завершение отрисовки (размер выходных данных, высота содержимого), запуск браузера (путь к исполняемому файлу), перезапуск браузера (счётчик отрисовок) и закрытие браузера (счётчик отрисовок). Это единственные генерируемые события, и они не содержат полезной нагрузки. Используйте их для целевых уровней обслуживания (SLO) по задержке и для оповещений о частоте перезапусков.
Соответствие
Заголовок раздела «Соответствие»| Утверждение | Источник | clause_id (идентификатор пункта) | reference_id (идентификатор ссылки) |
|---|---|---|---|
| Исходящие запросы серверных компонентов должны контролироваться | OWASP ASVS 5.0 | § (SSRF/outbound control — контроль исходящих запросов) | |
| SSRF = сервер извлекает переданный URL без проверки назначения | перечень CWE Top 25 2025 (CWE-918) | cwe_top25_2025#x28.x2.p2 | |
| SSRF (CWE-918) входит в перечень CWE Top 25 | перечень CWE Top 25 2025 | cwe_top25_2025#x1.p73 | |
| Неконтролируемое потребление ресурсов входит в перечень CWE Top 25 | перечень CWE Top 25 2025 (CWE-400) | cwe_top25_2025#x19.x2.p2 | |
| Защита границы по принципу “запрет по умолчанию” (разрешение в порядке исключения) | стандарт NIST SP 800-53 ред. 5, SC-7 | SC-7 | |
| Запрет на сетевом уровне вызовов к произвольным назначениям — сильный механизм контроля SSRF | серия памяток OWASP Cheat Sheet Series (SSRF Prevention §Network layer — предотвращение SSRF, сетевой уровень) | owasp_cheatsheet_series#x132.x2 | |
| Защита компонентов, загружающих URL, от SSRF | серия памяток OWASP Cheat Sheet Series | § (SSRF prevention, URL-fetch tools — предотвращение SSRF, средства загрузки по URL) | |
| Изоляция отрисовки недоверенного содержимого, наименьшие привилегии | OWASP ASVS 5.0 | § (sandbox / least privilege — песочница / наименьшие привилегии) | |
| Вести журнал эксплуатационных событий; не помещать полезные нагрузки в журналы | NIST SP 800-92 | § (log content guidance — рекомендации по содержимому журналов) |
Цитаты получены через систему соответствия NextPDF (манифест корпуса 1d05b7c4…d790b6); текст пунктов перефразирован, а не приведён дословно.
Модель угроз
Заголовок раздела «Модель угроз»| Угроза | Механизм контроля | Остаточный риск |
|---|---|---|
| SSRF через удалённый подресурс | CSP default-src 'none' + CDP setBlockedURLs('*') | Ошибка движка Chrome, которая обходит оба барьера (эшелонированная защита снижает риск, но не устраняет его) |
| SSRF через навигацию meta-refresh | Проверка до Chrome отклоняет тег | Новый вектор навигации, не соответствующий шаблону |
| Исчерпание ресурсов | Ограничения размера входных данных + ограничения Base64 + тайм-аут + перезапуск каждые 100 отрисовок | Нет поузловой квоты; сочетайте с cgroup/ulimit |
| Компрометация процесса средства отрисовки | Песочница Chrome когда включена | noSandbox: true полностью удаляет этот механизм контроля |
| Выход из стилей / внедрение | </style> удаляется в defaultCss; CSP блокирует скрипты | Внедрение через будущий вектор, который не будет удалён |
Поведение в режиме FIPS
Заголовок раздела «Поведение в режиме FIPS»Мост не выполняет никаких криптографических операций. Он создаёт байты PDF через Chrome и встраивает их. Подписание, шифрование и поведение в режиме Federal Information Processing Standards (FIPS) относятся к ядру/Premium и не затрагивают Artisan.
См. также
Заголовок раздела «См. также»- Настройка — /integrations/artisan/configuration/
- Настройка средства отрисовки Chrome — /integrations/artisan/chrome-renderer-setup/
- Устранение неполадок — /integrations/artisan/troubleshooting/
- Использование в продакшене — /integrations/artisan/production-usage/
- Обзор — /integrations/artisan/overview/