tags:

views:

74

answers:

3

I have the following scenario (C#, WinForms). I have some kind of project file which is saved in some directory. The project file contains a reference to another file. This reference is relative from the place where the project file is saved.

Sample: The project file is saved under c:\projects\project.xyz. The other file is referenced as "\someotherdir\file.abc".

This works fine, but there may be the case someone tried to manipulate that relative path to something like "..\Windows\System32\file.abc". So there's a need to check whether the relative path points outside the path where the project is saved (it's a defined requirement, that all referenced files are inside the project path).

How to detect this scenario?

Thanks, Michael

+2  A: 

Not very pretty but I think it should work.

if (System.IO.Path.GetFullPath(path).IndexOf(projectPath, StringComparison.CurrentCultureIgnoreCase) == -1)
{
  // naughty
}

Edited to be a good global citizen.

ho1
**Do not** implement case-insensitive string comparisons with ToUpper/ToLower. It fails the Turkey Test. Use one of the String.IndexOf overloads with a StringComparison parameter.
dtb
+1 for naughty :D
Matt Ellen
also use Path.GetDirectoryName(projectPath) instead of projectPath
Orsol
For those not being familiar with what the Turkey Test is: http://www.codinghorror.com/blog/2008/03/whats-wrong-with-turkey.html
0xA3
@dtb: Yes, too used to UK only apps where I never have to worry about that.
ho1
@Orsol: I meant projectPath as the path to the directory rather than the path to the actual project file, maybe should have been clearer.
ho1
+4  A: 

You could try using the following extension method:

public static bool IsChildOf(this string path, string parentPath)
{
    return Path.GetFullPath(path).StartsWith(Path.GetFullPath(parentPath),
           StringComparison.InvariantCultureIgnoreCase);
}
Ed Courtenay
This seems the best solution for me. Works fine, thanks.
Michael
This solution has one problem as further tests has showed: When the parent path is somethin like: "C:\dir1\dir2\test" and the path is something like "C:\dir1\dir2\test\..\test.abc" then GetFullPath returns "C:\dir1\dir2\test.abc" which should return false because the file is one level above the parentPath. But it returns true, because of StartsWith...
Michael
@Michael: After you've converted the it to an absolute path, use `Path.GetDirectoryName` to only get the directory part of the file you're testing.
ho1
I had changed this already, it was just meant as informations for other readers. Additionally there are more things to check in this scenario to make it really secure, but that would be out of scope of this question.
Michael
+1  A: 

Windows has posix symlinks: ln -s c:\windows\system32\mshtml.dll c:\projects\project.xyz\innocent.txt. When your program opens c:\projects\project.xyz\innocent.txt you get c:\windows\system32\mshtml.dll. Does System.IO.Path.GetFullPath() work here?

POSIX also supports hardlinks. A file can have zero (when deleted), one, two, ten, one hundred filenames. And all are "The Filename", none more correct or less correct than any other.

Windows supports mounting folders into folders. Again, all names are correct.

You can solve this with filesystem permissions: Create a new user for your application. Give that user permissions to your project path. Do not give that user (or Everyone, or any groups the user is a member of) privileges to anything else in any filesystem. Let Microsoft's kernel team solve your problem for you.

sarnold
Good to know, but in our case the can have multiple projects in different locations. So it would be neccessary that the application would set that rights for that special user for each project. This would require that the application would run with administration privileges, which is'nt a good solution.
Michael