class MTimeProtectedFastFileStorage extends FileStorage (View source)

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.

This class does not protect against overwriting a file in-place (e.g. a malicious module that does a file_put_contents()) since this will not change the mtime of the directory. MTimeProtectedFileStorage protects against this 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 removed) to assist with debugging, since the file itself is stored with a name that's meaningless to humans.

Properties

protected string $directory

The directory where the files should be stored.

from  FileStorage
protected string $secret

The secret used in the HMAC.

Methods

__construct(array $configuration)

Constructs this MTimeProtectedFastFileStorage object.

bool
exists(string $name)

Checks whether the PHP code exists in storage.

load(string $name)

Loads PHP code from storage.

bool
save(string $name, $data)

Saves PHP code to storage.

ensureDirectory(string $directory, int $mode = 0777)

Ensures the directory exists, has the right permissions, and a .htaccess.

bool
createDirectory(string $directory, int $mode = 0777)

Ensures the requested directory exists and has the right permissions.

bool
delete(string $name)

Deletes PHP code from storage.

string|false
getFullPath(string $name, string $directory = NULL, int $directory_mtime = NULL)

Gets the full path where the file is or should be stored.

bool
writeable()

Whether this is a writable storage.

deleteAll()

Removes all files in this bin.

bool
unlink(string $path)

Deletes files and/or directories in the specified path.

array
listAll()

Lists all the files in the storage.

garbageCollection()

Performs garbage collection on the storage.

string
getContainingDirectoryFullPath(string $name)

Gets the full path of the containing directory where the file is or should be stored.

getUncachedMTime($directory)

Clears PHP's stat cache and returns the directory's mtime.

string
tempnam($directory, $prefix)

A brute force tempnam implementation supporting streams.

Details

__construct(array $configuration)

Constructs this MTimeProtectedFastFileStorage object.

Parameters

array $configuration

An associative array, containing at least these two keys:

  • directory: The directory where the files should be stored.
  • bin: The storage bin. Multiple storage objects can be instantiated with the same configuration, but for different bins..

bool exists(string $name)

Checks whether the PHP code exists in storage.

Parameters

string $name

The virtual file name. Can be a relative path.

Return Value

bool

TRUE if the virtual file exists, FALSE otherwise.

load(string $name)

Loads PHP code from storage.

Depending on storage implementation, exists() checks can be expensive, so this function may be called for a file that doesn't exist, and that should not result in errors. This function does not return anything, so it is up to the caller to determine if any code was loaded (for example, check class_exists() or function_exists() for what was expected in the code).

Parameters

string $name

The virtual file name. Can be a relative path.

bool save(string $name, $data)

Saves PHP code to storage.

Parameters

string $name

The virtual file name. Can be a relative path.

$data

Return Value

bool

TRUE if the save succeeded, FALSE if it failed.

protected ensureDirectory(string $directory, int $mode = 0777)

Ensures the directory exists, has the right permissions, and a .htaccess.

For compatibility with open_basedir, the requested directory is created using a recursion logic that is based on the relative directory path/tree: It works from the end of the path recursively back towards the root directory, until an existing parent directory is found. From there, the subdirectories are created.

Parameters

string $directory

The directory path.

int $mode

The mode, permissions, the directory should have.

protected bool createDirectory(string $directory, int $mode = 0777)

Ensures the requested directory exists and has the right permissions.

For compatibility with open_basedir, the requested directory is created using a recursion logic that is based on the relative directory path/tree: It works from the end of the path recursively back towards the root directory, until an existing parent directory is found. From there, the subdirectories are created.

Parameters

string $directory

The directory path.

int $mode

The mode, permissions, the directory should have.

Return Value

bool

TRUE if the directory exists or has been created, FALSE otherwise.

bool delete(string $name)

Deletes PHP code from storage.

Parameters

string $name

The virtual file name. Can be a relative path.

Return Value

bool

TRUE if the delete succeeded, FALSE if it failed.

string|false getFullPath(string $name, string $directory = NULL, int $directory_mtime = NULL)

Gets the full path where the file is or should be stored.

This function creates a file path that includes a unique containing directory for the file and a file name that is a hash of the virtual file name, a cryptographic secret, and the containing directory mtime. If the file is overridden by an insecure upload script, the directory mtime gets modified, invalidating the file, thus protecting against untrusted code getting executed.

Parameters

string $name

The virtual file name. Can be a relative path.

string $directory

(optional) The directory containing the file. If not passed, this is retrieved by calling getContainingDirectoryFullPath().

int $directory_mtime

(optional) The mtime of $directory. Can be passed to avoid an extra filesystem call when the mtime of the directory is already known.

Return Value

string|false

The full file path for the provided name. Return FALSE if the implementation needs to prevent access to the file.

bool writeable()

Whether this is a writable storage.

Return Value

bool

deleteAll()

Removes all files in this bin.

Deletes files and/or directories in the specified path.

If the specified path is a directory the method will call itself recursively to process the contents. Once the contents have been removed the directory will also be removed.

Parameters

string $path

A string containing either a file or directory path.

Return Value

bool

TRUE for success or if path does not exist, FALSE in the event of an error.

array listAll()

Lists all the files in the storage.

Return Value

array

Array of filenames.

garbageCollection()

Performs garbage collection on the storage.

The storage may choose to delete expired or invalidated items.

protected string getContainingDirectoryFullPath(string $name)

Gets the full path of the containing directory where the file is or should be stored.

Parameters

string $name

The virtual file name. Can be a relative path.

Return Value

string

The full path of the containing directory where the file is or should be stored.

protected getUncachedMTime($directory)

Clears PHP's stat cache and returns the directory's mtime.

Parameters

$directory

protected string tempnam($directory, $prefix)

A brute force tempnam implementation supporting streams.

Parameters

$directory

The directory where the temporary filename will be created.

$prefix

The prefix of the generated temporary filename.

Return Value

string

Returns the new temporary filename (with path), or FALSE on failure.