3

If a user was requesting a file on a service, I could normally protect them from accessing documents outside the scope of what I want by using PHP's realpath() and ensuring it is under the root directory. Such as like this:

$path = realpath($_GET['path']);
// Protect against LFI vulnerabilities
if (substr($path, 0, strlen($root)) == $root)
{
    // safe
}

However, realpath() only works on files that already exist. What if I want to ensure that the location the user is about to have my script write to is under the root?

I can't use realpath(), should I just check and strip out '..' references? Or is there a better way?

Chris Foster
  • 2,639
  • 2
  • 23
  • 30
  • You could `basename` the path, and check if that's in your root. I assume that your users will be writing to an existing upload directory, or something such — if not, that obviously won't work. – Waleed Khan Jul 25 '12 at 17:52
  • The application will raise an error if they try to write to a directory that does not exist, so it is safe to assume that the directory will exist. I am aware of `basename`, but not how that would be helpful. Are you able to give a code example? – Chris Foster Jul 25 '12 at 17:57
  • Whoops, I meant dirname: `if (substr(dirname($path, 0, strlen($root)) == $root)`. – Waleed Khan Jul 25 '12 at 17:59
  • From the docs: "dirname() operates naively on the input string, and is not aware of the actual filesystem, or path components such as '..'", I also tried it and verified that it does not resolve components like '..' – Chris Foster Jul 25 '12 at 18:08
  • If the user controls what directory the files go under, then you could just strip out any "../" characters from the input, and work with that. If you control what directory they upload to, just check for a valid filename - [a-z][A-Z][-_] should be all you allow, with the provision for an extension. Makes your job a lot easier. – Karthik Rangarajan Jul 26 '12 at 15:15
  • Yeah, unfortunately I think that's the only solution. If you make that as an answer, I'll select it if noone has any other ideas. :) – Chris Foster Jul 26 '12 at 17:50

1 Answers1

0

How about checking if realpath(dirname($file)) is under the root directory?

$dir = $path;
$found = false;

while ($dir) {
    $dir = dirname($dir);
    if (!is_dir($dir)) {
        continue;
    }

    if (strpos(realpath($dir), $root) === 0) {
        $found = true;
    }

    break;
}

var_dump($found);
Gunther Konig
  • 172
  • 1
  • 7