MTimeProtectedFileStorage.php

Same filename and directory in other branches
  1. 9 core/lib/Drupal/Component/PhpStorage/MTimeProtectedFileStorage.php
  2. 8.9.x core/lib/Drupal/Component/PhpStorage/MTimeProtectedFileStorage.php
  3. 10 core/lib/Drupal/Component/PhpStorage/MTimeProtectedFileStorage.php

Namespace

Drupal\Component\PhpStorage

File

core/lib/Drupal/Component/PhpStorage/MTimeProtectedFileStorage.php

View source
<?php

namespace Drupal\Component\PhpStorage;


/**
 * Stores PHP code in files with securely hashed names.
 *
 * The goal of this class is to ensure that if a PHP file is replaced with
 * an untrusted one, it does not get loaded. Since mtime granularity is 1
 * second, we cannot prevent an attack that happens within one second of the
 * initial save(). However, it is very unlikely for an attacker exploiting an
 * upload or file write vulnerability to also know when a legitimate file is
 * being saved, discover its hash, undo its file permissions, and override the
 * file with an upload all within a single second. Being able to accomplish
 * that would indicate a site very likely vulnerable to many other attack
 * vectors.
 *
 * Each file is stored in its own unique containing directory. The hash is
 * based on the virtual file name, the containing directory's mtime, and a
 * cryptographically hard to guess secret string. Thus, even if the hashed file
 * name is discovered and replaced by an untrusted file (e.g., via a
 * move_uploaded_file() invocation by a script that performs insufficient
 * validation), the directory's mtime gets updated in the process, invalidating
 * the hash and preventing the untrusted file from getting loaded. Also, the
 * file mtime will be checked providing security against overwriting in-place,
 * at the cost of an additional system call for every load() and exists().
 *
 * The containing directory is created with the same name as the virtual file
 * name (slashes replaced with hash marks) to assist with debugging, since the
 * file itself is stored with a name that's meaningless to humans.
 */
class MTimeProtectedFileStorage extends MTimeProtectedFastFileStorage {
    
    /**
     * {@inheritdoc}
     */
    public function load($name) {
        if (($filename = $this->checkFile($name)) !== FALSE) {
            // Inline parent::load() to avoid an expensive getFullPath() call.
            return @(include_once $filename) !== FALSE;
        }
        return FALSE;
    }
    
    /**
     * {@inheritdoc}
     */
    public function exists($name) {
        return $this->checkFile($name) !== FALSE;
    }
    
    /**
     * Determines whether a protected file exists and sets the filename too.
     *
     * @param string $name
     *   The virtual file name. Can be a relative path.
     *
     * @return string|false
     *   The full path where the file is if it is valid, FALSE otherwise.
     */
    protected function checkFile($name) {
        $filename = $this->getFullPath($name, $directory, $directory_mtime);
        return file_exists($filename) && filemtime($filename) <= $directory_mtime ? $filename : FALSE;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getPath($name) {
        return $this->checkFile($name);
    }

}

Classes

Title Deprecated Summary
MTimeProtectedFileStorage Stores PHP code in files with securely hashed names.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.