3 namespace Drupal\Component\PhpStorage;
6 * Stores PHP code in files with securely hashed names.
8 * The goal of this class is to ensure that if a PHP file is replaced with
9 * an untrusted one, it does not get loaded. Since mtime granularity is 1
10 * second, we cannot prevent an attack that happens within one second of the
11 * initial save(). However, it is very unlikely for an attacker exploiting an
12 * upload or file write vulnerability to also know when a legitimate file is
13 * being saved, discover its hash, undo its file permissions, and override the
14 * file with an upload all within a single second. Being able to accomplish
15 * that would indicate a site very likely vulnerable to many other attack
18 * Each file is stored in its own unique containing directory. The hash is
19 * based on the virtual file name, the containing directory's mtime, and a
20 * cryptographically hard to guess secret string. Thus, even if the hashed file
21 * name is discovered and replaced by an untrusted file (e.g., via a
22 * move_uploaded_file() invocation by a script that performs insufficient
23 * validation), the directory's mtime gets updated in the process, invalidating
24 * the hash and preventing the untrusted file from getting loaded. Also, the
25 * file mtime will be checked providing security against overwriting in-place,
26 * at the cost of an additional system call for every load() and exists().
28 * The containing directory is created with the same name as the virtual file
29 * name (slashes replaced with hashmarks) to assist with debugging, since the
30 * file itself is stored with a name that's meaningless to humans.
32 class MTimeProtectedFileStorage extends MTimeProtectedFastFileStorage {
37 public function load($name) {
38 if (($filename = $this->checkFile($name)) !== FALSE) {
39 // Inline parent::load() to avoid an expensive getFullPath() call.
40 return (@include_once $filename) !== FALSE;
48 public function exists($name) {
49 return $this->checkFile($name) !== FALSE;
53 * Determines whether a protected file exists and sets the filename too.
56 * The virtual file name. Can be a relative path.
58 * @return string|false
59 * The full path where the file is if it is valid, FALSE otherwise.
61 protected function checkFile($name) {
62 $filename = $this->getFullPath($name, $directory, $directory_mtime);
63 return file_exists($filename) && filemtime($filename) <= $directory_mtime ? $filename : FALSE;
69 public function getPath($name) {
70 return $this->checkFile($name);