0

Say you want to store a file within a folder C:\A\B\C and let the user supply the file name.

Just combine them, right?

Wrong.

If the user selects something like \..\..\Ha.txt you might be in for a surprise.

So how do we restrict the result to within C:\A\B\C? It's fine if it's within a subfolder, just not over it.

ispiro
  • 26,556
  • 38
  • 136
  • 291
  • So, you need the root to be `C:\A\B\C`? – McNets May 18 '22 at 19:43
  • 1
    This is why you don't ever trust the user's input and validate data coming in. File names can't contain slashes (either direction). – gunr2171 May 18 '22 at 19:44
  • @McNets Basically. Yes. – ispiro May 18 '22 at 20:05
  • Normalize (https://stackoverflow.com/questions/1266674/how-can-one-get-an-absolute-or-normalized-file-path-in-net) and check for path to be under desired one (like https://stackoverflow.com/questions/11605871/see-if-file-path-is-inside-a-directory)... Hopefully someone will write correct answer that does that... instead of regex or even worse some direct checks on file system (which are ok solutions, just not useful for case when you can't trust the input) – Alexei Levenkov May 18 '22 at 21:07

2 Answers2

1

If you're asking for a file name, then it should be just the name of the file. The more control you give to the user about subdirectories, the more they can mess with you.


The idea here is to split your path by both possible slashes (/ and \) and see if the value of any of the entries in the array is ...

string input = @"\..\..\Ha.txt";
bool containsBadSegments = input
    .Split(new [] { '/', '\\' })
    .Any(s => s is "..");

This answer only takes care of detecting \..\ in the path. There are plenty of other ways to input bad values, such as characters not allowed by the OS's file system, or absolute or rooted paths.

gunr2171
  • 16,104
  • 25
  • 61
  • 88
  • This is a cat and mouse game. Like using Regex to sanitize an SQL query instead of using parameterized queries. If there's nothing better, so be it. But I'm hoping for something air-tight. But thanks anyway! – ispiro May 18 '22 at 20:19
1

I've used one of my test projects, it really doesn't matter:

Using c#10

internal class Program
{
    static void Main(string[] args)
    {
        string template = @"F:\Projectes\Test\SourceGenerators";
        string folder = @"..\..\..\..\Test1.sln";

        Console.WriteLine(MatchDirectoryStructure(template, folder) 
                          ? "Match" 
                          : "Doesn't match");
    }

    static bool MatchDirectoryStructure(string template, string folder) 
        => new DirectoryInfo(folder).FullName.StartsWith(template);
}

As you can see, new DirectoryInfo(fileName).FullName; returns the real name of the directory.

From here you can check if it match with the desired result.

In this case the returned value is:

Match
McNets
  • 10,352
  • 3
  • 32
  • 61
  • Note that code (and approach) suggested in this answer *should not* be used in real life. Checking for existence of a file and more importantly reporting different values if file exist anywhere on system vs file is outside expected path ("File does not exist" vs "Doesn't match"). For any real check that should be preventing directory traversal attacks one should stick to path/string manipulations and never actually try accessing file as provided by user without validating rules first. – Alexei Levenkov May 18 '22 at 21:04
  • @AlexeiLevenkov I've used File.Exist only to find the proper location of my file. – McNets May 18 '22 at 21:12
  • This seems like the best there is so far, since I can't find a dedicated method for doing this. – ispiro May 19 '22 at 16:43