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.

Marco Acierno
  • 14,682
  • 8
  • 43
  • 53
Matt Welander
  • 8,234
  • 24
  • 88
  • 138
  • 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! – GrahamTheDev Jan 06 '14 at 15:12
  • use `realpath` function to eliminate symlink such as `.`, `..`, `/`, `//`, etc as appeared in the Gumbo's suggestion. – Ruwanka De Silva Jan 06 '14 at 15:24

4 Answers4

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.

elitechief21
  • 2,964
  • 1
  • 17
  • 15
  • 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! – Matt Welander Jan 06 '14 at 15:43
  • 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? – The Surrican Jan 06 '14 at 15:57
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.

The Surrican
  • 29,118
  • 24
  • 122
  • 168
  • 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? – Matt Welander Jan 06 '14 at 16:04
  • 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. – The Surrican Jan 06 '14 at 16:06
  • 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? – Matt Welander Jan 06 '14 at 16:09
  • 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. – The Surrican Jan 06 '14 at 18:06
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;
}
Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
RaggaMuffin-420
  • 1,762
  • 1
  • 10
  • 14
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

user2226755
  • 12,494
  • 5
  • 50
  • 73
  • 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... – The Surrican Jan 06 '14 at 15:55