ASP.NET MVC Get file from virtual path - c#

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.

Related

c# - Physical path given, virtual path expected in Server.MapPath

I trying to change my directory which in my local c disk, but where errors says in the title. Is there any way aside from using Server.MapPath?. I'm using a ZipOutputStream nuget package.
I want to locate my directory in C: instead inside the project folder.
public FileResult DownloadZipFileSig(string FileId){
var fileName = "FilesDL".zip";
var tempOutPutPath = Server.MapPath(Url.Content("C:/Users/SDILAP2/Desktop/ID_Esig_Files")) + fileName;
using (ZipOutputStream s = new ZipOutputStream(System.IO.File.Create(tempOutPutPath)))
{
s.SetLevel(9);
byte[] buffer = new byte[4096];
List<string> stringList = FileId.Split(',').ToList();
List<string> tempList = new List<string>();
foreach (string str in stringList)
{
if (System.IO.File.Exists(Server.MapPath("C:/Users/SDILAP2/Desktop/ID_Esig_Files/" + str + ".jpeg")))
{
tempList.Add(Server.MapPath("C:/Users/SDILAP2/Desktop/ID_Esig_Files/" + str + ".jpeg"));
}
}
stringList = tempList;
for (int i = 0; i < stringList.Count; i++)
{
ZipEntry entry = new ZipEntry(Path.GetFileName(stringList[i]));
entry.DateTime = DateTime.Now;
entry.IsUnicodeText = true;
s.PutNextEntry(entry);
using (FileStream fs = System.IO.File.OpenRead(stringList[i]))
{
int sourceBytes;
do
{
sourceBytes = fs.Read(buffer, 0, buffer.Length);
s.Write(buffer, 0, sourceBytes);
} while (sourceBytes > 0);
}
}
s.Finish();
s.Flush();
s.Close();
}
return File(finalResult, "application/zip", fileName);
}
You might be not quite grasping how web URL's work, and how server.mappath() is to be used.
Web users:
When you have a web based url, then all html markup in a page, or even user supplied URL's are so called web based.
So, if you have a folder from the root of your web site say called MyUpLoads
Then that is just a folder in the web site path names.
eg:
www.mywebsite/UpLoadFiles/cat.jpg
And if you write html markup, then you can and could provide a URL to the above picute, or say with a html image control, you could set the ImageURL or "source" (src) to that file.
And if you using IIS (and not IIS express), then of course you can add what is called a virutal folder. Say some big server drive on ANOHTER computer on the same network.
So, that virtual folder could be anywhere on your network, and of course AGAIN for web HTML, or web URL's, again you use this format:
www.mysite/MassiveFolder/info.pdf
or maybe
localhost:5403/MyUpLoads/cat.jpg
However, in code behind?
ANY code behind (c# or vb.net) ALWAYS uses plane jane WINDOWS file paths.
These are valid full windows file names.
That means that code behind is 100% free to open/read/use/see/play with ANY file on the computer, and any file even on the computer network.
So when you use
server.mapPath("localhost:5403/MyUpLoads/cat.jpg")
Then the above is translated into a local plane jane DOS/WINDOWS file path!!!!
The above may well become
C:\Users\AlbertKallal\source\repos\CSharpWebApp\MyUpLoads\cat.jpg
So keep in mind:
web urls - HTML/asp markup in a page = web based syntax/path.
computer path: plane jane full path names like all windows software.
So, in your case?
var fileName = "FilesDL".zip";
var tempOutPutPath = #"C:/Users/SDILAP2/Desktop/ID_Esig_Files")) + fileName;
So you don't need nor want to user server.mappath, since that is ONLY for a given HTML or web based URL that you want to translate into the local computer file path system.
Since your path name(s) are already in that format, then no need is required.
in fact, keep in mind that you can use this fact to your advantage.
ANY folder (or a vitural folder) will appear in your valid URL's and path names (web based).
However, you might have some pdf's, or sensitive documents. So move that folder OUT of the root or web project folders.
Now, no valid URL's exist, or are even allowed.
However, code behind? It can run, see and use ANY file on your computer - and you use code behind to get those files - but the web site, web side of things has NO ability to use or see or get those files. And you can still do things like say provide a download button, but your code behind can fetch the file, read it and pump it out to the end user (stream the file).
So you only need (have) to use the Server.MapPath function WHEN the URL comes from the web site or html markup. This will translate that web based URL into a regular good old fashion full qualified windows file path name.
However, if you already have that full windows path name, then no URL translate to windows file path is required.
So, for the most part, your code behind can look at, see, grab and play with files on the server. Web users, or web based urls MUST be part of the folders in the web site, but no such restrictions exist for the code behind.
Now, when the code is deployed to a web server, often some file security rights on in place, but as a general rule, that web code behind is NOT limited nor restricted to JUST folders in the web site. Those valued URL's are a restriction for the users and web browsers, and as noted, often a folder outside of the web site is used for security purposes, since no possible valid web based paths can use/see or even resolve to file outside of the root starting folder of the web site.
So for those existing files, you don't need server.mappath.

C# winforms get nth folders from path

I've got an absolute path available to me. Say: C:/Program/CoreFiles/Folder1/Folder2/File.txt.
I need to copy that file to C:/Program/Projects/ProjectName/ but it needs to keep Folder1/Folder2/File.txt intact. So the end result should be C:/Program/Projects/ProjectName/Folder1/Folder2/File.txt.
My first attempt at solving this was to try and get the relative path between 2 absolute paths. Found Path.GetRelativePath(string, string) which obviously didn't help as it wasn't meant for WinForms. It would mess up anyway as the final result would be C:/Program/Projects/ProjectName/CoreFiles/Folder1/Folder2/File.txt.
The target directory is empty and I don't know the relative path to copy beforehand other than somehow getting that info out of the absolute path. Since File.Copy won't create folders that don't exist yet, I need to create them first. So how do I get the path that leads up to the file from the CoreFiles directory out of the absolute path?
The only working solution I can come up with is using regex to just replace CoreFiles with Projects/ProjectName in the path string and work with that. But that somehow seems the wrong approach.
Since you can't use Path.GetRelativePath. I suggest looking at another answer that describes how to do this yourself.
Like here...
How to get relative path from absolute path
Using the method in that answer, you can do the rest of your task as shown below.
string sourcePath = "C:/Program/CoreFiles/Folder1/Folder2/File.txt";
string sourceRoot = "C:/Program/CoreFiles/";
string destinationRoot = "C:/Program/Projects/ProjectName/";
// Use built-in .NET Path.GetRelativePath if you can. Otherwise use a custom function. Like here https://stackoverflow.com/a/340454/1812944
string relativePath = MakeRelativePath(sourceRoot, sourcePath);
// Combine the paths, and make the directory separators all the same.
string destinationPath = Path.GetFullPath(Path.Combine(destinationRoot, relativePath));
// Create nested folder structure for your files.
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
// Copy the file over.
File.Copy(sourcePath, destinationPath);

Libragnar(Libtorrent Wrapper) LocalTorrent File, Instead of URL? C#/C++

Question:
Does anyone know how to add a torrent to LibRagnar using a filepath to a torrent, instead of a Url? (LibRagnar is a libtorrent Wrapper)
libragnar = C#
libtorrent = C++
Alternatively if anyone knows How I can use Libtorrent To add the torrent to a session, But use a local file (Whilst still controlling Everything else using Libragnar).But I am not sure where to start with Libtorrent.
Reason For Problem:
I have to use a filepath because the Torrent Requires cookie login to access it. So I either Need to get Libragnar to use a CookieCollection when getting a torrent from a URL or make it use a local ".torrent" file.
Problem:
I am currently trying to use a filepath instead of URL and the Torrent Status gives an error:unsupported URL protocol: D:\Programming\bin\Debug\Tempfiles\File.torrent. Which wont allow me to start it.
Example:
var addParams = new AddTorrentParams
{
SavePath = "C:\\Downloads",
Url = "D:\\Programming\\bin\\Debug\\Tempfiles\\File.torrent"
};
Edit: Answer from Tom W (Posted in C# Chatroom)
var ati = new AddTorrentParams()
{
TorrentInfo = new TorrentInfo("C:\thing.torrent"),
SavePath = #"C:\save\"
};
Note About Answer: I attempted to edit Tom W's post and add the answer he gave me in the Chatroom, However I guess it got declined? But Since he was the one who helped me I wanted him to get credit, and also wanted anyone else having this issue, to have an answer. So I had to add the answer to the bottom of my question.
From the libtorrent documentation it appears that:
The only mandatory parameters are save_path which is the directory
where you want the files to be saved. You also need to specify either
the ti (the torrent file), the info_hash (the info hash of the
torrent) or the url (the URL to where to download the .torrent file
from)
Libragnar's AddTorrentParams appears to be a wrapper around add_torrent_params and has a property called TorrentInfo. I suspect if you avoid setting the URL, and set this property to an instance of TorrentInfo instead, you ought to get the result you want.
Disclaimer: I've never worked with torrents before, don't know this library, and don't work in C++.

How do I make sure a file path is safe in C#?

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();
}

Securely enforcing user-input file paths within subdirectories

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.

Categories

Resources