Unrestricted File Download in PHP
Vulnerable Example
The example below shows a vulnerable Symfony controller that serves files from a directory in an unsecure way:
class FetchFileController extends Controller
{
const ROOT = '/path/to/root/';
/**
* @Route("/", name="fetch_file_index")
* @Method("GET")
*/
public function indexAction(Request $request)
{
$file_name = $request->query->get('filename');
$file_data = @file_get_contents(self::ROOT . "/$file_name");
return $this->render('fetch_file/index.html.twig', ['file_data' => $file_data]);
}
}
The file_name
query parameter is controlled by the user; it is possible to retrieve arbitrary files in the filesystem by injecting ../
in the file_name
parameter as shown below:
http://example.com/fetch_file/?file_name=../../../../../etc/passwd
This results in the absolute path /path/to/root/../../../../../etc/passwd
, and its canonicalized form: /etc/passwd
.
Prevention
Make sure to avoid using the user-provided value to escape the designed directory by means of ../
. The basename
function can be used to extract the base component of a path.
To remediate the vulnerability in the previous example, the $file_name
variable should be fixed as follows:
$file_name = basename($file_name);
The previous attack would result in reading the /path/to/root/passwd
file (which does not exist).