Problem/Motivation
Drupal may be configured to allow untrusted users to upload files with arbitrary filenames, including filenames with path components. For instance, if Drupal is installed at /var/www/
and the uploaded file repository is at /var/www/sites/example.com/files/
, then an uploaded filename of '../../../index.php'
would point to the /var/www/index.php
file.
To prevent untrusted users from overwriting such critical files, the current logic for the DrupalLocalStreamWrapper::getLocalPath()
function works like this:
Set
$directory
to therealpath()
of the files repository.Set
$path
to the target file location.Set
$realpath
torealpath($path)
.Return
FALSE
if$realpath
does not begin with$directory
.
This causes a problem when the site builder wants to store some uploaded files outside of the Drupal web root. Many site owners have resorted to hard-linked directories, recursive bind mounts, or simply hacking Drupal core to remove the restriction.
Proposed resolution
The logic within the DrupalLocalStreamWrapper::getLocalPath()
function should be revised as follows:
Set
$directory
to therealpath()
of the files repository.Set
$path
to the target file location.Set
$realpath
torealpath($path)
.Return
FALSE
if both of the following are true:$path
contains'/..'
or'\..'
.$realpath
does not begin with$directory
.
Remaining tasks
The patch proposed in #65 needs to be reviewed by the security team.
Task | Novice task? | Contributor instructions | Complete? |
---|---|---|---|
Update the issue summary noting if allowed during the beta | Instructions |
User interface changes
None.
API changes
The DrupalLocalStreamWrapper::getLocalPath()
function would follow symlinks, even if they point outside the files repository.
Original report by tekante
Related issue: #155781: "realpath" check breaks symbolic links in file directory
The method by which directory ascension attacks are prevented in stream_wrappers.inc prevents file system layouts which rely on symlinks within the files directory. Specifically, the use of realpath within the getLocalPath results in the check of whether the destination directory exists within the files directory to fail.
I will attach a patch which allows for the destination directory to exist outside of the configured files directory when it appears this is due to the presence of a symlink and not due to directory ascension characters.