1

I have a constant beginning of a string, and a variable ending, how can I secure the string so that is doesn't create a step-back (or step-up) in case the string I inject contains

../

Here is a short sample code:

$dir = 'my/base/path/';
$file = $dir . $userSelectedFilename;
unlink($file);

If $userSelectedFilename would be '../../myFileName' I assume that would cause my script to actually try to unlink something two directory levels up my/myFilename which is clearly not something I want to allow, I want to keep it under the basepath my/base/path/ under all circumstances.

2
  • my/base/path/../../myFileName - I might be being thick but on my server at least that wont go up a level it will just result in an error! Commented Jan 6, 2014 at 15:12
  • use realpath function to eliminate symlink such as ., .., /, //, etc as appeared in the Gumbo's suggestion. Commented Jan 6, 2014 at 15:24

4 Answers 4

1

I suggest the the following, and only the following method:

<?
$dir = 'my/base/path/';
$file = $dir . $userSelectedFilename;
if(strpos(realpath($file),realpath($dir)) === 0) && is_file($file)) { // beware of the three ===
  unlink($file);
}

Why?

It is safe to rely on realpath to find out the real location of a file which eliminates directory traversal / multibyte double-dots etc.

After that we check whether the beginning of the reapath of the file is really the beginning of our expacted directory (strpos).

After that we also check whether the file is really a file and not some symlink pointing elswhere or something like that.

I have seen character eliminating solutions been broken by multibyte strings and similar attacks.

This method so far far withstands all of these.

Sign up to request clarification or add additional context in comments.

4 Comments

I see your point. If I were to disallow dots all together in my $userSelectedFilename would that protect me? Or could the slash alone produce directory traversing?
you dont need to check for dots any longer, the solution above takes care of this. the user can specify folder1/folder1/../ and will be in folder 1. it is okay as long as the user stays inside the specified directory.
Yes, but if I only allow safe strings to begin with I only have to do it in one place (I'm lazy) =) So will no-dots-policy keep me safe?
if your strings are transliterated to ascii and contain no double dots and the target is really a file and not a symlink or diretory and also all directoryies in that tree, then yes - you probably are.
1

You could filter out those characters by doing something like:

$file = preg_match("/\.\.\//", "", $file)

which will remove occurrences of the string ../

And just a side note, you should probably find a different way of allowing users to select files to delete rather than allowing them to input the path as a string, maybe by showing them a directory listing of files they can delete or something like that.

2 Comments

Thank you, I will use regexp. As for your sidenote - that's a good suggestion but that is not what I am doing, I just simplified my case a bit to focus on the problem. Many thanks!
true, it filters the path. but it does not really check whether the file is in the directory. i know this is not part of the answer,but it has to be kept in mind. what about a symlink inside the directory pointing outside?
0

You can do this "my/base/path/../../dir/", if you want "real" path use this :

 echo realpath($dir."../../dir/"); // my/dir

http://php.net/manual/en/function.realpath.php

1 Comment

true, but its also important on how to work with the result of realpath in this case. i have seen it done wrong in many cases...
0

Using regex to validate the string for ../ or /../ and not accepting the string if the regex returns true:

function validatePath($path) {
    if(preg_match('#\/\.\.\/#',$path))
        return False;
}

1 Comment

throw in a multi byte .. and you are doomed.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.