Skip to content

Issue with realpath() and open_basedir() altogether #2707

@pounard

Description

@pounard

This issue is a reddit of #2387 which is probably experiencing a side effect of the same bug.

Context is, I am developping against Symfony 3.4 with some application. Our admins, which are sometime (maybe too much) paranoid always do set open_basedir PHP directive, on a per-project basis, in order to avoid PHP breaking out its rightful scope.

Ou application uses Symfony's 3 style (not Symfony 4-flex style) so we have the following folders in our directory structure:

  • /srv/http/someproject/app/Resources/views
  • /srv/http/someproject/composer.json
  • /srv/http/someproject/var/cache
  • ...

Problem here lies in the \Twig_Loader_Filesystem::__construct(), which does the following:

    public function __construct($paths = array(), $rootPath = null)
    {
        $this->rootPath = (null === $rootPath ? getcwd() : $rootPath).DIRECTORY_SEPARATOR;
        if (false !== $realPath = realpath($rootPath)) {
            $this->rootPath = $realPath.DIRECTORY_SEPARATOR;
        }

Since we have open_basedir restriction over the project, which basically is set to this (and a few more folders that show no interest in being here): /srv/http/someproject/app:/srv/http/someproject/var/cache, this means that when \Twig_Loader_Filesystem::__construct() is called with the $rootPath parameter set to /srv/http/someproject/, it tries a realpath() call outside of the allowed open_basedir folders.

So I have here a major problem, because it's hardcoded, hence our sysadmins can't enforce a proper security sandbox for the projets.

I also a real question, the \Twig_Loader_Filesystem::$rootPath property behing private, and having no public accessor, this means that it has never been meant to be used outside of a few very specific use cases I spotted:

  • in addPath() and prependPath() it serves the purpose of ensuring that all paths are absolute (heck, why not) - but in real life, in Symfony's use case, we do always end up with path already being absolute (so there is no purpose in that),

  • in getCacheKey() having an absolute path seems to make the cache key even more unique, so okay, why not, it makes sense,

  • in findTemplate() it serves the purpose of absoluting each registered template path, which seems a duplicate of doing it within addPäth() and therefore is always a no-op (I think it could be removed, also it would potentially be a minor BC break under very peculiar conditions, but not in Symfony as I recall).

Are those realpath() really necessary ?

In my situation, I can see two solutions to fix this:

  • a quick fix is to simple remove the realpath() call in the constructor, and from this, I can a see a single an only one potential side effect: if your CLI and your HTTPd don't see the same path (for example, if the HTTPd context is chrooted and the CLI context is not) it will create different cache keys for both. This by extension would mean that warming template caches from CLI would not be possible anymore.

  • or a better one, add an optional $noRealpath constructor argument, which would turn to be a micro optimization for Symfony's framework, that in our case seems to feed the loader with only absolute paths from the begining (making those realpath() calls always being obsolete).

In all cases, in the current state of things, twig prevents sysadmins from correctly securing/sandboxing their PHP applications, and that's bad.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions