Twig в контенте¶
Grav может обрабатывать Twig внутри Markdown-контента страницы, поэтому в теле страницы можно писать выражения вроде {{ ... }} и теги вроде {% ... %}. В Grav 2.0 это поведение изменилось по соображениям безопасности: Twig в контенте теперь по умолчанию отключён, а когда вы его включаете, контент выполняется в песочнице безопасности.
Примечание
Эта страница посвящена Twig, который пишется внутри контента страницы. Файлы .html.twig в вашей теме и плагинах — это доверенный код на диске, и они никогда не помещаются в песочницу. Ничто на этой странице не влияет на обычную разработку тем или плагинов.
Что изменилось в 2.0¶
В Grav 1.7 любая страница с process.twig: true в метаданных выполняла свой контент через Twig без ограничений. Поскольку контент страниц пишет любой, у кого есть доступ на редактирование, редактор страницы мог написать Twig, который читал секреты конфигурации, обращался к файловой системе или, в худшем случае, выполнял код на сервере. Этот класс уязвимостей называется инъекцией шаблонов на стороне сервера (SSTI).
Grav 2.0 закрывает эту дыру двумя независимыми уровнями защиты:
- Разрешающий механизм. Twig в контенте вообще не выполняется, пока администратор явно не включит его в настройках безопасности.
- Песочница. Когда Twig в контенте всё-таки выполняется, разрешён только список безопасных тегов, фильтров, функций, методов и свойств. Всё остальное блокируется.
Эти уровни независимы. Разрешающий механизм решает, будет ли выполняться Twig в контенте, а песочница — что он сможет делать, если запустится. Включение разрешающего механизма не отключает песочницу.
Включение Twig в контенте¶
1. Выберите профиль¶
В админке перейдите в Конфигурация → Безопасность. В верхней части страницы находится единый селектор профиля Twig в контенте, который охватывает типичные случаи:
| Профиль | Действие |
|---|---|
| Выкл. | Twig в контенте никогда не выполняется. Значение по умолчанию. |
| Только доверенные роли | Twig в контенте выполняется, но включить его на странице могут только суперпользователи и обладатели разрешения admin.pages_twig. |
| Все редакторы | Twig в контенте выполняется, и любой пользователь с доступом на редактирование страниц может включить его на редактируемых страницах. |
| Пользовательский | Показывается, только если сохранённые настройки не совпадают с именованным профилем (например, изменённая вручную комбинация). Выбор именованного профиля заменяет его. |
Выбор профиля записывает за вас два базовых ключа, поэтому напрямую их трогать почти никогда не нужно. Они остаются доступны ниже селектора в расширенном виде:
| Настройка | По умолчанию | Действие |
|---|---|---|
| Обработка включена | Выкл. | Главный переключатель. Пока он выключен, Twig в контенте не выполняется независимо от метаданных любой страницы. |
| Редактор включён | Выкл. | Определяет, кто может переключать process.twig из редактора страницы. Выкл.: только пользователи с разрешением admin.pages_twig (или суперпользователь). Вкл.: любой с доступом на редактирование страниц. |
Эквивалентные настройки в user/config/security.yaml:
Профили сопоставляются с этими ключами так: Выкл. — оба выключены; Только доверенные роли — process_enabled: true и editor_enabled: false; Все редакторы — оба включены.
2. Понимайте, что включает разрешающий механизм¶
Когда Обработка включена, разрешающий механизм становится единственным источником истины для Twig в контенте, и каждая страница по умолчанию обрабатывает Twig в своём контенте. Это изменение по сравнению с 1.7, где каждая страница подключалась индивидуально через свои метаданные. Больше не нужно отмечать страницы по одной.
Если нужно, чтобы конкретная страница не выполняла Twig в своём контенте, исключите эту страницу в её метаданных:
Явное значение process.twig на странице (true или false) всегда важнее разрешающего механизма, поэтому Twig по-прежнему можно принудительно включить для одной страницы, но делать это приходится редко. Поскольку переключатель включает Twig в контенте на всём сайте, включайте его осознанно.
Сохранение литеральных {{ }} в контенте¶
Когда Twig в контенте включён, Grav вычисляет каждое {{ ... }} и {% ... %} в контенте страницы. Это проблема, если двойные фигурные скобки предназначены не для Grav, например для поля формы Formspree или для шаблона Vue, Handlebars или Angular, который вы встраиваете. Grav пытается вычислить выражение, ничего не находит, и фигурные скобки исчезают из вывода.
Есть два способа сохранить литеральные фигурные скобки:
- Исключить всю страницу из Twig в контенте с помощью
process.twig: falseв её метаданных (показано выше). - Обернуть только затронутую разметку в тег
verbatim, чтобы Grav не трогал её, пока остальная часть страницы по-прежнему обрабатывает Twig:
{% verbatim %}
<input type="hidden" name="subject" value="Feedback: {{ user_subject }} {{ topic }}" />
{% endverbatim %}
При установке по умолчанию Twig в контенте выключен, поэтому эти фигурные скобки и так проходят без изменений, и экранирование не требуется.
3. Очистите кэш¶
После изменения настроек безопасности или параметра process страницы очистите кэш, чтобы страницы перерендерились:
Предупреждение
Если Обработка включена выключена, а страница задаёт process.twig: true, Twig этой страницы не рендерится. Необработанная разметка {{ ... }} и {% ... %} возвращается как литеральный текст, а в logs/security.log записывается уведомление.
Песочница¶
Когда Twig в контенте выполняется, он работает в песочнице, определённой в twig_sandbox файла system/config/security.yaml. Из коробки песочница разрешает широкий набор безопасных Twig-конструкций только для чтения, что покрывает большинство реальных задач:
- Теги для управления потоком и композиции:
if,for,set,block,include,embed,macro,apply,with,verbatimи другие. - Фильтры, такие как
date,upper,lower,default,markdown,json_encode,number_format,nicetime,slug,length,join,sortи многие другие. - Функции, такие как
url,authorize,t,media_directory,theme_var,nicetime,dump, а также стандартные помощники Twig вродеrange,min,maxиcycle. - Методы и свойства только для чтения обычных объектов: текущий
page(title, header, media, children, collection, taxonomy и т. д.), егоmedia,uri, активныйuserи активныйlanguage.
Полные списки по умолчанию находятся в system/config/security.yaml в разделе twig_sandbox; их можно просмотреть в админке в разделе Конфигурация → Безопасность → Песочница Twig.
Что происходит при блокировке¶
Песочница завершается мягко, не выбрасывая фатальную ошибку:
- Заблокированное выражение выводится как литеральный текст, поэтому видно, что именно было остановлено.
- В
logs/security.logзаписывается строка с именем заблокированного тега, фильтра, функции, метода или свойства, маршрутом страницы и подсказкой, как его разрешить. - То же событие фиксируется для отчёта Twig в контенте (см. ниже), поэтому не нужно читать файл журнала, чтобы узнать, что было заблокировано.
- Если вы вошли как суперпользователь-администратор и включён
admin_hint, Grav добавляет HTML-комментарий рядом с заблокированным выражением, указывающий на журнал.
Так что если после включения разрешающего механизма страница показывает сырое {{ something() }}, значит, песочница заблокировала something(). Отчёт назовёт точный нераспознанный элемент и способ его разрешить.
Проверка XSS в контенте¶
Валидатор XSS в схеме проверяет необработанный контент, который вы сохраняете, поэтому полезная нагрузка, собранная во время рендеринга (например, {{ "on" ~ "error" }}, которая становится onerror только после выполнения Twig), проскальзывает мимо него. Grav закрывает этот пробел при сохранении страницы: он изолированно рендерит собственный Twig редактора через тот же проход с песочницей, описанный выше, запускает по результату детектор XSS и отклоняет сохранение, если отрендеренная разметка опасна. Отказ записывается в logs/security.log.
Поскольку проверка выполняется при сохранении и видит только собственный вывод редактора, разметка шорткодов, плагинов или темы никогда не проверяется, а просмотр страницы не требует дополнительных затрат. Учётные записи, покрытые security.xss_whitelist (по умолчанию суперпользователи-администраторы), освобождены от проверки, как и при валидаторе исходного кода.
Сопоставляемые шаблоны — это те, что указаны в security.xss_enabled и security.xss_dangerous_tags в system/config/security.yaml, тот же список, который использует bin/grav security.
Примечание
Для этой проверки нет настройки, и отдельного сканирования готовой страницы во время рендеринга тоже нет. В Grav 2.0.1–2.0.10 оно было и управлялось security.twig_content.xss_scan_output, а позже security.content.xss_scan_output. Оба ключа были удалены в Grav 2.0.11 вместе с security.xss_allowed_iframe_hosts и событиями onXssTrustedMarkup / onXssAllowedIframeHosts, которые питали только его; при обновлении они автоматически удаляются из user/config/security.yaml.
Отчёт Twig в контенте¶
Каждый тихий сбой на этой странице (страница с выключенным разрешающим механизмом, блокировка песочницей, утечка сырого Twig) собирается в одном месте админки: Инструменты → Отчёты → Twig в контенте. Это избавляет от ручного чтения logs/security.log. Отчёт показывает:
- Текущее состояние с первого взгляда: включены ли разрешающий механизм и песочница.
- Страницы, отдающие сырой Twig. Любая страница, контент которой содержит
{{ ... }}или{% ... %}и не будет отрендерен (потому что разрешающий механизм выключен или на этой странице Twig отключён), перечисляется с понятной причиной и прямой ссылкой на редактор страницы. - Недавние блокировки. Последние блокировки разрешающего механизма и песочницы, каждая с маршрутом страницы и той же подсказкой, что попадает в журнал. Блокировка песочницы сопровождается кнопкой Добавить в список разрешённых, которая добавляет заблокированный элемент в нужный список за вас, сохраняя полный существующий список (см. Настройка песочницы).
- Сканировать контент. Кнопка, которая сканирует контент каждой страницы на теги, фильтры и функции, не разрешённые песочницей в данный момент, чтобы вы увидели, что нужно вашему контенту, до включения разрешающего механизма, а не обнаруживали это страница за страницей.
Когда вы открываете страницу в редакторе, та же информация появляется встроенным баннером в верхней части страницы, если её контент отдаёт сырой Twig или недавно столкнулся с блокировкой, со ссылкой на полный отчёт.
Примечание
На странице может быть process.twig: true (часто остаётся от сайта Grav 1.7), а разрешающий механизм при этом выключен. В 2.0 этот флаг — лишь запрос: последнее слово всё равно за разрешающим механизмом, поэтому такие страницы выводят сырой текст. Отчёт явно указывает на это, чтобы вы могли либо выбрать профиль и включить Twig, либо удалить устаревший флаг.
Настройка песочницы¶
Если вы решили, что конкретный элемент Twig безопасно выполнять против контента, который могут писать ваши авторы, его можно добавить в список разрешённых. Сначала важное предупреждение о том, как объединяются эти списки.
Списки разрешённых песочницы не складываются при ручном редактировании. Плоские списки (allowed_functions, allowed_filters, allowed_tags) полностью заменяются тем, что вы укажете под этим ключом в user/config/security.yaml, а списки по классам (allowed_methods, allowed_properties) объединяются по позиции, поэтому частичный список молча перезаписывает существующие записи. Если написать так:
то будут удалены все встроенные безопасные функции (url, t и остальные), и останется только my_safe_function, что сломает гораздо больше, чем исправит. Есть три безопасных способа добавить элемент:
- Использовать отчёт (самый простой). Когда элемент заблокирован, отчёт Twig в контенте (в разделе Инструменты → Отчёты) показывает его с кнопкой Добавить в список разрешённых. Один клик добавляет его в нужный список с сохранением всех существующих записей. Это самый быстрый безопасный путь, потому что вы действуете с точным элементом, который только что заблокировал Grav.
- Использовать админку. В разделе Конфигурация → Безопасность → Песочница Twig каждое поле предварительно заполнено текущим полным списком. Добавьте свою запись и сохраните — Grav запишет обратно полный список, поэтому значения по умолчанию не потеряются.
- Редактировать вручную с полным списком. Скопируйте весь список по умолчанию для нужного ключа из
system/config/security.yamlв свойuser/config/security.yaml, затем добавьте новую запись, сохранив все существующие.
То же правило действует для списков методов и свойств по классам: включайте все существующие строки class/methods (или class/properties) вместе с добавляемыми.
twig_sandbox:
allowed_methods:
- class: 'Grav\Plugin\MyGallery\Gallery'
methods: 'render, thumbnail'
# ...плюс все строки по умолчанию, скопированные из system/config/security.yaml
Разработчики плагинов могут программно регистрировать собственные безопасные элементы через событие onBuildTwigSandboxPolicy, поэтому плагин работает в контенте с песочницей без необходимости каждому сайту редактировать security.yaml. Это надёжное решение для элементов, предоставляемых плагинами, и оно полностью избавляет от необходимости поддерживать полные списки. Сигнатуру события и рабочий пример см. в руководстве по обновлению для разработчиков.
Предупреждение
Разрешайте только элементы, безопасные для выполнения против контента, который может написать любой человек с доступом на редактирование страниц. Добавление элемента в список разрешённых — такое же решение о доверии, как и его изначальное предоставление. Если функция читает файлы, вычисляет строки или обращается к контейнеру Grav, не добавляйте её в список.
Переменная config¶
По умолчанию переменная Twig config пуста внутри контента с песочницей, поэтому авторы страниц не могут читать конфигурацию вашего сайта (которая может содержать секреты). Если нужен доступ на чтение к неконфиденциальной конфигурации из контента, включите Доступ к конфигурации в разделе Twig в контенте или задайте twig_content.config_access: true. При включении config становится отфильтрованным фасадом, который всё равно скрывает конфиденциальные поддеревья, перечисленные в twig_sandbox.config_denied_paths (по умолчанию: plugins, streams, security, backups, scheduler).
Отключение песочницы¶
Песочницу можно полностью отключить с помощью twig_sandbox.enabled: false, что снимает всю защиту от SSTI для контента, написанного редакторами. Это настоятельно не рекомендуется на любом сайте, где страницы могут редактировать несколько полностью доверенных людей. Почти всегда существует более узкая настройка (добавление конкретного элемента в список разрешённых), которая решает реальную задачу, не снимая защиту.
Лучший путь: вынести Twig из контента¶
Песочница существует для случаев, когда Twig в контенте действительно необходим, но рекомендуемый подход в Grav 2.0 — по возможности избегать Twig в контенте. Он смешивает логику представления с обычным текстом, затрудняет безопасное редактирование для нетехнических авторов и привязывает контент к внутренностям шаблонов. Почти все случаи покрывают два подхода.
Использовать шаблон страницы¶
Если одной странице нужна логика шаблона, создайте для неё отдельный шаблон в теме и укажите его через template: в метаданных. Шаблоны доверенные и не помещаются в песочницу, и логика шаблона должна находиться именно там. Тело страницы снова становится чистым Markdown.
Использовать шорткод¶
Для повторяемых, удобных для авторов фрагментов обычно лучше подходит собственный шорткод. Вместо того чтобы учить авторов фрагменту Twig (и добавлять в список разрешённых всё, что ему нужно), вы даёте им один короткий читаемый тег, а вся логика живёт в PHP вашего плагина. В контенте Twig не выполняется, поэтому песочнице нечего блокировать.
Пошаговое руководство «сначала Twig, потом шорткод» см. в разделе Создание шорткода. Плагин сообщества Shortcode Core также предоставляет большой набор готовых шорткодов.