Skip to content

[3.x, 4.x, 5.x]: Template::attribute() bypasses Twig SandboxExtension's SecurityPolicy #15278

@khalwat

Description

@khalwat

What happened?

Description

The Template::attribute() method (which is what is called whenever accessing an object property in the PHP compiled from Twig) does the following:

        if (
            $type !== TwigTemplate::METHOD_CALL &&
            $object instanceof BaseObject &&
            $object->canGetProperty($item)
        ) {
            return $isDefinedTest ? true : $object->$item;
        }

This bypasses the call later in that function to twig_get_attribute() , which means that if the Twig SandboxExtension is used with a SecurityPolicy, the SecurityPolicy::checkPropertyAllowed() method is never called.

This makes it impossible to implement a SecurityPolicy that restricts access to properties on any object that is an instance of BaseObject (which just about everything in Craft is)

For instance, if you wanted to restrict access to:

{{ craft.app.config.db.password }}

...you can't do it, because the Twig layer that implements sandbox security checks is never called, because the DbConfig object inherits from BaseObject, so the property value is just returned.

Steps to reproduce

  1. Add the SandboxExtension to the Twig environment
  2. Use a SecurityPolicy that implements ::checkPropertyAllowed()
  3. Notice that it is never called when accessing an object property like entry.title on any BaseObject object

Expected behavior

It would be possible to use the SandboxExtension with a SecurityPolicy that restricts access to object properties.

This fixes the issue, allowing the SandboxExtension to optionally throw an exception before the property is returned:

        if (
            $type !== TwigTemplate::METHOD_CALL &&
            $object instanceof BaseObject &&
            $object->canGetProperty($item)
        ) {
            if ($sandboxed) {
                $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source);
            }
            return $isDefinedTest ? true : $object->$item;
        }

This mirrors what twig_get_attribute() does.

Twig\Sandbox\SecurityNotAllowedPropertyError
Accessing "password" property on a "craft\config\DbConfig" object is not allowed in "__string_template__ac8af96bb981e782c298f5d7796307e3" at line 1.

Actual behavior

It's not possible :)

Craft CMS version

3.x, 4.x, 5.x

PHP version

n/a

Operating system and version

n/a

Database type and version

n/a

Image driver and version

n/a

Installed plugins and versions

n/a

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions