I know the solid security recommendation of avoiding accepting user input that you then use to choose a path to read/write a file. However, assuming you have a base directory you want to keep within (such as the root of an ftp folder), how do you best ensure that a given user input keeps us within that folder?
For instance,
Path.Combine(_myRootFolder, _myUserInput)
could still take us outside of _myRootFolder. And this could also be dodgy
newPath = Path.Combine(_myRootFolder, _myUserInput)
if (newPath.StartsWith(_myRootFolder))
...
given something like "/back/to/myrootfolder/../../and/out/again" from the user. What are the strategies for this? Am I missing a blindingly obvious .NET method I can use?
Within ASP.NET applications you can use Server.MapPath(filename) which will throw an exception if the path generated goes outside of your application root.
If all you want is a safe file name and you just want all files in there it becomes simpler;
FileInfo file = new FileInfo(
Server.MapPath(
Path.Combine(#"c:\example\mydir", filename)));
If you're outside of ASP.NET like you indicate then you could use Path.GetFullPath.
string potentialPath = Path.Combine(#"c:\myroot\", fileName);
if (Path.GetFullPath(potentialPath) != potentialPath)
// Potential path transversal
Or you call Path.GetFullPath and then check the start of it matches the directory you want locked to.
I know, that this thread is quiet old, but to prevent following readers from writing code with potential security errors, I think I should point out, that using Path.Combine(arg1, arg2) isn't save when arg2 is directly based on user input.
When arg2 is for example "C:\Windows\System32\cmd.exe" the arg1 parameter will be completely ignored and you grant the users of your API or server application full access to the whole file system.
So please be very careful with using this method!
I came up with this solution that should (afaik) be secure:
public static string SecurePathCombine(params string[] paths)
{
string combinedPath = "";
foreach (string path in paths)
{
string newPath = Path.Combine(combinedPath, path);
if (!newPath.StartsWith(combinedPath))
return null;
combinedPath = newPath;
}
if (Path.GetFullPath(combinedPath) != combinedPath)
return null;
return combinedPath;
}
Edit: There is a new Path.Join() method now. Please use that one instead of the code above.
I believe Path.FullPath will do what you need (I didn't test this though):
string newPath = Path.Combine(_myRootFolder, _myUserInput);
string newPath = Path.FullPath(newPath);
if (newPath.StartsWith(_myRootFolder)) ...
Well, in your example of an FTP server, you should set the users home-directory, and permissions appropriately, such that they can't navigate out of the folder. Any reason you can't do that?
You can parse input string and cut ../ with regex.
Related
I've allowed for custom paths to be entered and wanted the default to be something along the lines of:
%UserProfile%/Documents/foo, of course this needs to successfully parse the string and though it will work in Windows Explorer, I was wondering if I'm missing a library call or option for parsing this correctly.
DirectoryInfo's constructor certainly doesn't work, treating %UserProfile% like any other folder name.
If there is no good way, I'll manually parse it to substitute %foo% with the actual special folder location if it is in the Special Folders Enumeration.
Edit:
Code that does what I'm looking for (though would prefer a proper .NET library call):
var path = #"%UserProfile%/Documents/foo";
var specialFolders = Regex.Matches(path, "%(?<possibleSpecial>.+)%");
foreach (var spec in specialFolders.AsEnumerable())
{
if (Enum.TryParse<Environment.SpecialFolder>(spec.Groups["possibleSpecial"].Value, out var sf))
{
path = Regex.Replace(path, spec.Value, Environment.GetFolderPath(sf));
}
}
Use Environment.ExpandEnvironmentVariables on the path before using it.
var pathWithEnv = #"%UserProfile%/Documents/foo";
var path = Environment.ExpandEnvironmentVariables(pathWithEnv);
// your code...
Question Background:
I need to copy and paste (move) a file from one folder location to another.
Issue:
The File.Copy method of System.IO requires the that both parameters are of known file locations. I only know one file path location - in this case localDevPath. localQAPath is the folder path where I want the copied file to be moved too.
string localDevPath = #"C:\Folder1\testFile.cs";
string localQaPath = #"C:\Folder2\";
File.Copy(localDevPath, localQaPath);
Can anyone tell me how to go about carrying out this 'copy and paste' method I'm trying to implement.
string localDevPath = #"C:\Folder1\testFile.cs";
string localQaPath = #"C:\Folder2\";
FileInfo fi = new FileInfo(localDevPath);
fi.MoveTo(Path.Combine(localQaPath, fi.Name));
Assuming that these are user-provided paths and you can't simply include the filename in the second path, then you need to extract the last path element from localDevPath and then add it to localQaPath. You could probably do that with Path.GetFilename.
I'm guessing the issue here is that the filename is variable, in which case, you could do something like this to extract the filename from the full path of localDevPath:
string localDevPath = #"C:\Folder1\testFile.cs";
string localQaPath = #"C:\Folder2\";
string[] tokens = localDevPath.Split(#"\");
localQaPath += tokens[tokens.Length-1];
File.Copy(localDevPath, localQaPath);
Documentation on File.Copy is on MSDN. There is an overload that accepts a boolean, to allow overwriting if there is a naming conflict.
If what you want to do is move the file from one location to another, the method you are looking for is MoveTo. It is a method of the FileInfo class. There is a very complete example in the MSDN Library here: FileInfo.MoveTo Example
I'm using C# to get the exact path of the specific folder in windows system by giving the folder name. Is their any way to get the folder path by giving the folder name, where the folder name will be unique.
Update:
Folder is created at run time with current time as the name. This
process is done by the application. Here i know the folder name but i
didn't know path, because path is selected by the user during
installation and installation is done before very long time.
That changes the question considerably. Why not use the application to tell you where it lives:
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.startuppath.aspx
I had a similar idea ages ago and wrote about it as a Code Project Tip:
http://www.codeproject.com/Tips/132804/Open-folders-using-a-Run-Command
Otherwise you would need to index every folder on the PC and make them unique names and look up the full path that way.
The other suggestion I have is using LogParser as the Most efficient way to find all exe files on disk using C#? Its a free Microsoft product but I'm not sure about re-dist permissions, I had to include it in my package separately last time I used it. It full on flys, faster than a speeding train!
I found a Log Parser example that finds folders, you could try it out and adapt it if its useful:
SELECT TOP 1 * FROM C:\TFS\Project\*.* WHERE INDEX_OF(Path, 'Database') > 0
The good folks over at http://visuallogparser.codeplex.com/ have
provided us with the source code.
Open the VisualLogParser solution in VS2010, ignore the prompt about debugging, after the solution loads, F5, set the combo-box to FS (FileSystem), paste in this query and press go.
You could probably use something like this, but it'll be rather slow, depending on how many folders needed to be looked through.
Use it like FindFullPath(rootFolder, folderNameToLookFor)
public static string FindFullPath(string path, string folderName)
{
if (string.IsNullOrWhiteSpace(folderName) || !Directory.Exists(path))
{
return null;
}
var di = new DirectoryInfo(path);
return findFullPath(di, folderName);
}
private static string findFullPath(DirectoryInfo directoryInfo, string folderName)
{
if (folderName.Equals(directoryInfo.Name, StringComparison.InvariantCultureIgnoreCase))
{
return directoryInfo.FullName;
}
try
{
var subDirs = directoryInfo.GetDirectories();
return subDirs.Select(subDir => findFullPath(subDir, folderName)).FirstOrDefault(fullPath => fullPath != null);
}
catch
{
// DirectoryNotFound, Security, UnauthorizedAccess
return null;
}
}
See following link
string dirName = new DirectoryInfo(#"c:\projects\roott\wsdlproj\devlop\beta2\text").Name;
For various reasons, in development I occasionally want to intercept a request for, say, ~/MyStyle.css
What I want to do is make the following snippet work:
string absFile = VirtualPathUtility.ToAbsolute(file);
return System.IO.File.ReadAllText(absFile);
This absolute path is absolute for the webserver though, it's not going to map to "C:\whatever". Is there an equivalent method to go to the file system? (Or a ReadFromVirtualPath etc.?)
Use Server.MapPath() to get the file system path for a requested application path.
string absFile = Server.MapPath(file);
or
string absFile = HttpContext.Current.Server.MapPath(file);
You can also use the OpenFile method on VirtualPathProvider to get a Stream pointing at your file
var stream = HostingEnvironment.VirtualPathProvider.OpenFile(file);
var text = new StreamReader(stream).ReadToEnd();
Generally this approach is preferable since you can now, at a later point implement a VirtualPathProvider where, lets say all your css files where located in a database.
I have an asp.net mvc app with a route that allows users to request files that are stored outside of the web application directory.
I'll simplify the scenario by just telling you that it's going to ultimately confine them to a safe directory to which they have full access.
For example:
If the user (whose ID is 100) requests:
http://mysite.com/Read/Image/Cool.png
then my app is going to append "Cool.png" to "C:\ImageRepository\Users\100\" and write those bytes to the response. The worker process has access to this path, but the anonymous user does not. I already have this working.
But will some malicious user be able to request something like:
http://mysite.com/Read/Image/..\101\Cool.png
and have it resolve to
"C:\ImageRepository\Customers\101\Cool.png"
(some other user's image?!)
Or something like that? Is there a way to make sure the path is clean, such that the user is constrained to their own directory?
How about
var fileName = System.IO.Path.GetFileName(userFileName);
var targetPath = System.IO.Path.Combine(userDirectory, fileName);
That should ensure you get a simple filename only.
Perhaps you should verify that the path starts with the user's directory path?
e.g. "C:\ImageRepository\Customers\100\"
You should also normalize the paths to uppercase letters when comparing them.
The safest way, if it is an option (you are using windows auth), is to make it a non-issue by using Active Directory rights on the folders so it doesn't matter if the user attempts to access a directory that is not valid.
Absent that, store the files so that the path is abstracted from the user. That is, use whatever name the user provides as a lookup in a table that has the REAL path to the file.
Cannolocalization protection is tricky business and it is dangerous to try and outthink a potential attacker.
Using the Request.MapPath overload is one way to check this:
try
{
string mappedPath = Request.MapPath( inputPath.Text, Request.ApplicationPath, false);
}
catch (HttpException)
{
// do exception handling
}
Also you could explode the string and delimit it by slashes, and check the username match also.
To also be able to include a subdirectory in the path you can use:
string SafeCombine(string basePath, string path)
{
string testPath = Path.GetFullPath(Path.Combine(basePath, path));
if (testPath.startsWith(basePath))
return testPath;
throw new InvalidOperationException();
}