PHP realpath not working to prevent directory traversal

3.5k views Asked by At

I have received someone else's code for a system that shows folders with photos in them in your browser.

For example, this is a possible url on the site:

gallery.php?action=view&folder=Cars

At the moment, you can replace "Cars" with ../../../../../, which will happily show the Linux root folder. Luckily, the website is offline at the moment.

I tried using realpath to fix this. Here's what I have so far:

case 'view' :
$name = realpath(dirname(__FILE__) . "/Content/Gallery/" . $_GET['folder']);

echo $name;

$data = $file->getPictures($name);
require 'Views/Gallery.view.php';
break;

I have added echo on the third line to see what the URL would be. In the url above, everything is fine, and echo outputs this:

/var/www/Content/Gallery/Cars 

So far, so good. However, if I enter /../../../../../../../../ instead of "Cars", $name becomes / and the page still shows the root folder. English isn't my first language, so I could be misunderstanding how realpath works or what it does. From what I understood, it removes any instance of ../ from a given URL.

Can someone please tell me what I'm doing wrong?

2

There are 2 answers

2
Jessica On BEST ANSWER

From what I understood, it removes any instance of ../ from a given URL.

No, that's not what it does. It's not for URLs, it's for paths. It simply converts a path and expands ../ into the right folder. It doesn't remove them, it resolves them - meaning it calculates what the ../ stands for and alters the path to have that.

It also changes / to \ on Windows.

realpath

realpath() expands all symbolic links and resolves references to '/./', '/../' and extra '/' characters in the input path and returns the canonicalized absolute pathname.

0
Pekka On

realpath() alone is not sufficient to fight directory traversal.

What you describe is exaclty what it's designed to do: it translates relative things like ../ into the actual directory path.

However, for security, you can take a realpath() ed path and see whether it still is a child of a safe base path. If it isn't, somebody tried to sneak into a directory they have no business being in:

 $name = realpath(dirname(__FILE__) . "/Content/Gallery/" . $_GET['folder']);

 // The safe root directory. 
 $safe_root = dirname(__FILE__) . "/Content/Gallery/";

 // Check whether realpath() result is still inside /Content/Gallery
 if (substr($name, 0, strlen($safe_root)) != $safe_root) 
   die ("Possible directory traversal attack");