Files
Twig/doc/sandbox.rst
T
Fabien Potencier 8dd0383353 Merge branch '3.x' into 4.x
* 3.x: (23 commits)
  Bump version
  Prepare the 3.26.0 release
  Update CHANGELOG
  Document that the sandbox doesn't protect against resource exhaustion
  Document template_from_string caveats when used in a sandboxed env
  Pre-escape HTML input on the `spaceless` filter
  Add docs on Markup about the goal of this class in the context of a sandbox
  Fix sandbox bypass in the "column" filter
  Fix sandbox `__toString` bypasses
  Validate macro name in MacroReferenceExpression constructor
  Fix sandbox bypass: PHP code injection via _self / import macro reference
  Fix deprecations in tests
  Fix sandbox bypass in the `{% sandbox %}` tag when including a preloaded template
  Encode single quotes as \x27 in Compiler::string()
  Fix sandbox bypass: PHP code injection via {% use %} template name
  Fix unbounded memoisation of `IntlDateFormatter` / `NumberFormatter`
  Fix deprecation
  [Profiler] Escape template and profile names in HtmlDumper
  Bump version
  Fix sandbox bypass: propagate sandbox state to checkArrow for source-policy sandboxing
  ...

# Conflicts:
#	CHANGELOG
#	doc/filters/spaceless.rst
#	extra/cssinliner-extra/CssInlinerExtension.php
#	extra/inky-extra/InkyExtension.php
#	extra/markdown-extra/MarkdownExtension.php
#	src/Environment.php
#	src/ExpressionParser/Infix/DotExpressionParser.php
#	src/Extension/CoreExtension.php
#	src/Node/Expression/FilterExpression.php
#	src/Node/Expression/FunctionExpression.php
#	src/Node/Expression/TestExpression.php
#	src/Node/ModuleNode.php
#	src/NodeVisitor/SandboxNodeVisitor.php
#	src/Resources/core.php
#	src/TokenParser/SandboxTokenParser.php
#	tests/Extension/SandboxTest.php
2026-05-21 13:58:45 +02:00

114 lines
4.1 KiB
ReStructuredText

Twig Sandbox
============
The ``sandbox`` extension can be used to evaluate untrusted code.
Registering the Sandbox
-----------------------
Register the ``SandboxExtension`` extension via the ``addExtension()`` method::
$twig->addExtension(new \Twig\Extension\SandboxExtension($policy));
Configuring the Sandbox Policy
------------------------------
The sandbox security is managed by a policy instance, which must be passed to
the ``SandboxExtension`` constructor.
By default, Twig comes with one policy class: ``\Twig\Sandbox\SecurityPolicy``.
This class allows you to allow-list some tags, filters, functions, and
properties and methods on objects::
$tags = ['if'];
$filters = ['upper'];
$methods = [
'Article' => ['getTitle', 'getBody'],
];
$properties = [
'Article' => ['title', 'body'],
];
$functions = ['range'];
$policy = new \Twig\Sandbox\SecurityPolicy($tags, $filters, $methods, $properties, $functions);
With the above configuration, the security policy will only allow usage of the
``if`` tag, and the ``upper`` filter. Moreover, the templates will only be able
to call the ``getTitle()`` and ``getBody()`` methods on ``Article`` objects,
and the ``title`` and ``body`` public properties. Everything else won't be
allowed and will generate a ``\Twig\Sandbox\SecurityError`` exception.
.. note::
If the ``Article`` class implements the ``ArrayAccess`` interface, the
templates will only be able to access the ``title`` and ``body``
attributes.
Note that native array-like classes (like ``ArrayObject``) are always
allowed, you don't need to configure them.
Enabling the Sandbox
--------------------
By default, the sandbox mode is disabled and should be enabled when including
untrusted template code by using the ``sandboxed`` option of the ``include``
function:
.. code-block:: twig
{{ include('user.html.twig', sandboxed: true) }}
You can sandbox all templates by passing ``true`` as the second argument of
the extension constructor::
$twig->addExtension(new \Twig\Extension\SandboxExtension($policy, true));
Limiting Resource Usage
-----------------------
The sandbox prevents untrusted templates from reaching code, data, methods, or
properties they shouldn't. It does **not** prevent a template from consuming
CPU, memory, or wall-clock time, even under the strictest allow-list.
This is by design: any limit baked into Twig itself would be both arbitrary
and trivial to work around, since there are many ways a template can burn
resources (large ranges, nested loops, large string operations, recursive
macros, expensive filters, deeply nested includes, and so on).
If you render untrusted templates, you should contain them at the process level
rather than at the template engine level.
Accepting Callables Arguments
-----------------------------
The Twig sandbox allows you to configure which functions, filters, tests and
dot operations are allowed. Many of these calls can accept arguments. As these
arguments are not validated by the sandbox, you must be very careful.
For instance, accepting a PHP ``callable`` as an argument is dangerous as it
allows end user to call any PHP function (by passing a ``string``) or any
static methods (by passing an ``array``). For instance, it would accept any PHP
built-in functions like ``system()`` or ``exec()``::
$twig->addFilter(new \Twig\TwigFilter('custom', function (callable $callable) {
// ...
$callable();
// ...
}));
To avoid this security issue, don't type-hint such arguments with ``callable``
but use ``\Closure`` instead (not using a type-hint would also be problematic).
This restricts the allowed callables to PHP closures only, which is enough to
accept Twig arrow functions::
$twig->addFilter(new \Twig\TwigFilter('custom', function (\Closure $callable) {
// ...
$callable();
// ...
}));
{{ people|custom(p => p.username|join(', ') }}
Any PHP callable can easily be converted to a closure by using the `first-class callable syntax`_.
.. _`first-class callable syntax`: https://www.php.net/manual/en/functions.first_class_callable_syntax.php