I've been asked to see if it's possible to prevent the Content directory from appearing as part of the url in an Asp.Net MVC 3.0 application. For example at present when I want to view an image in the sub directory of the Content folder the url is as follows:
http://localhost:[port]/Content/sub/test.bmp
While we are looking to display it simply as follows:
http://localhost:[port]/sub/test.bmp
Test.bmp will still physically exist in the sub directory of the Content folder on the server we just want to hide the Content part.
Any suggestions? I can see ways of masking controllers but not directories.
You could write a controller action which will take as an argument the filename and serve it from the sub directory. Then configure a route for this controller action so that it is accessible with sub/{filename}.
Solution is as follows (this is just the barebones code at the moment and needs to be tidied up):
Added this route to Global.asax :
routes.MapRoute("Content",
"{dir}/{file}",
new { controller = "Content", action = "LoadContent"});
Added this controller to handle the request:
namespace demos
{
public class ContentController : Controller
{
public ActionResult LoadContent(string dir, string file)
{
string fileName = Server.MapPath(Url.Content("~/Content/" + dir))
fileName += "\\" + file;
// stream file if exists
FileInfo info = new FileInfo(fileName);
if (info.Exists)
return File(info.OpenRead(), MimeType(fileName));
// else return null - file not found
return null;
}
private string MimeType(string filename)
{
string mime = "application/octetstream";
var extension = Path.GetExtension(filename);
if (extension != null)
{
RegistryKey rk = Registry.ClassesRoot.OpenSubKey(extension.ToLower());
if (rk != null && rk.GetValue("Content Type") != null)
mime = rk.GetValue("Content Type").ToString();
}
return mime;
}
}
}
Related
I have an issue where potentials attackers can gain access to potentially sensitive information such as a web config file through one of my controller methods
public ActionResult GetPdfContent(string fileCode)
{
try
{
var relativePath = "~/files/content/" + fileCode;
if (!User.Identity.IsAuthenticated)
{
return RedirectToAction("Login", "Account");
}
if (System.IO.File.Exists(Server.MapPath(relativePath)))
{
return File("~/files/content/" + fileCode, "application/pdf", Server.UrlEncode(fileCode));
}
else
{
return View("ErrorNotExistsView");
}
}
}
As a recommendation the files codes should be white-listed for one.
Could it be as simple as adding a collection such as a list with all the white listed content and returning an error if the parameter is not contained within the list?
List<string> lstWhitelistedContent = new List<string>() { "code1", "code2", "code3"};
if (!lstWhitelistedContent.Contains(fileCode))
{
return View("ErrorNotExistsView");
}
Another approach would be to white-list a directory rather than list of files, and apparently your /files/content directory already seem like a good candidate.
Also, judging by the name of your method, it should only serve .pdf files, so you can add another restriction by the file extension.
Try this instead:
[Authorize]
public ActionResult GetPdfContent(string fileCode)
{
// this will remove path traversal attempts like '..'
var fileName = Path.GetFileName(fileCode);
// this will get you the file extension
var fileExtension = Path.GetExtension(fileName).ToLowerInvariant();
if (fileExtension != ".pdf")
return View("ErrorNotExistsView");
return File("~/files/content/" + fileName, "application/pdf", Server.UrlEncode(fileName));
}
}
Also, I took the liberty to remove the authorization check in the method and placed an [Authorize] attribute instead.
I am developing CMS for a website using MVC 5 and Entity Framework. I have an edit form for editing the events added in the database table. I am uploading the image related to Event in a server folder and storing its URL in database.
Now I want to replace the
#Html.EditorFor(model => model.Image_Url, new { htmlAttributes = new { #class = "form-control" } })
with a input with type File, so that anyone can change the image while editing the Event information. for this I have added following.
A Picture Class
public class Pictures
{
public HttpPostedFileBase File { get; set; }
}
for File Uploading in Edit Action Method of controller
if (picture.File.ContentLength > 0)
{
var fileName = Path.GetFileName(picture.File.FileName);
var path = Path.Combine(Server.MapPath("~/assets/uploads/events/"), fileName);
picture.File.SaveAs(path);
filePath = "~/assets/uploads/events/" + fileName;
}
and finaly in Edit view
<input type="file" id="File" name="File" class="form-control" />
The above logic work perfectly fine when used in Create action method, but when I use the same in edit action method the Null reference exception occur. While debugging I found that the picture.File parameter is null at line if (picture.File.ContentLength > 0).
This is working fine in Create but in Edit action method it returns null.
Any Help on this issue?
I have done it by using Request.Files. below is my code in controller Edit action method.
foreach(string fileName in Request.Files)
{
HttpPostedFileBase file = Request.Files[fileName];
fName = file.FileName;
if (file != null && file.ContentLength > 0)
{
var orgDirectory = new DirectoryInfo(Server.MapPath("~/assets/uploads"));
string pathString = System.IO.Path.Combine(orgDirectory.ToString(),"events");
var fileName1 = Path.GetFileName(file.FileName);
bool isExists = System.IO.Directory.Exists(pathString);
if(!isExists)
{
System.IO.Directory.CreateDirectory(pathString);
}
var path = string.Format("{0}\\{1}", pathString, file.FileName); //pathString + file.FileName;
file.SaveAs(path);
}
}
This issue could occur because of general security rules for the <input type="file" /> as you can't set a value for it. So I think the only thing you can do is a create separate view for the re-creating the image for the profile, and each time user sends a picture to you treat is a new one.
Im a bit new to Umbraco, and i have to say i like it a lot.
But now i'm stuck on something simple i think. I Created a protected page that is only visible to members on my website. Where the member is able to upload multiple files at once. This is al working like a charm. First i created the upload form for multiple images then i created the SurfaceController to handle the submit. Also working like a charm.
My ActionResult on my SurfaceController receives an IEnumerable<HttpPostedFileBase> called files which is good. I see all my images that i'm posting with my form. But here comes the problem.
While looping over my files I try to create a Media (Image type) using the MediaService.CreateMedia giving my filename and parentid and the mediaType (Image).
But when i try to set the umbracoFile value on my just created media item i will get the following exception:
An unhandled exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in Umbraco.Core.dll
Additional information: The best overloaded method match for
'Umbraco.Core.Models.ContentBase.SetPropertyValue(string, string)'
has some invalid arguments
I hope someone can tell my what i'm doing wrong. Below is my code i'm using
[HttpPost]
public ActionResult UploadFiles(IEnumerable<HttpPostedFileBase> files)
{
bool success = false;
//Get logged in member and look for the mediafolderID
var member = Services.MemberService.GetByUsername(HttpContext.User.Identity.Name);
var mediaFolderID = member.GetValue<int>("mediaFolderID");
//Get mediafolder
var mediaFolder = Services.MediaService.GetById(mediaFolderID);
try
{
// Create a media item from each file uploaded
foreach (var file in files)
{
var fileName = file.FileName; // Assumes no path information, just the file name
var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower();
if (!UmbracoConfig.For.UmbracoSettings().Content.DisallowedUploadFiles.Contains(ext))
{
var mediaType = global::Umbraco.Core.Constants.Conventions.MediaTypes.File;
if (UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.Contains(ext))
{
mediaType = global::Umbraco.Core.Constants.Conventions.MediaTypes.Image;
}
var f = Services.MediaService.CreateMedia(fileName, mediaFolderID, mediaType);
// Assumes the file.InputStream is a Stream - you may have to do some extra work here...
f.SetValue(global::Umbraco.Core.Constants.Conventions.Media.File,(Stream)file.InputStream); // Real magic happens here.
Services.MediaService.Save(f);
}
}
success = true;
}
catch (Exception ex)
{
// On error show message
ViewData["exceptionMessage"] = ex.Message;
success = false;
}
// On success redirect to current page and show successmessage
ViewData["success"] = success;
if (success)
{
return RedirectToCurrentUmbracoPage();
}
return CurrentUmbracoPage();
}
Instead of f.SetValue(global::Umbraco.Core.Constants.Conventions.Media.File, (Stream)file.InputStream); you should just use the HttpPostedFileBase: f.SetValue(global::Umbraco.Core.Constants.Conventions.Media.File, file);
Some other notes:
Check that the file has a length and is not null: file != null && file.ContentLength > 0
You're not using your mediaFolder variable anywhere, can be removed.
Not sure why you'd need global::Umbraco.Core, consider adding using Umbraco.Core; and use Constants.Conventions.MediaTypes.Image etc.
Check that you really need to rely on DisallowedUploadFiles - I'm pretty sure that's checked during CreateMedia
In my IHttpHandler class (for an .ashx page), I want to search a directory for certain files, and return relative urls. I can get the files, no problem:
string dirPath = context.Server.MapPath("~/mydirectory");
string[] files = Directory.GetFiles(dirPath, "*foo*.txt");
IEnumerable<string> relativeUrls = files.Select(f => WHAT GOES HERE? );
What is the easiest way to convert file paths to relative urls? If I were in an aspx page, I could say this.ResolveUrl(). I know I could do some string parsing and string replacement to get the relative url, but is there some built-in method that will take care of all of that for me?
Edit: To clarify, without doing my own string parsing, how do I go from this:
"E:\Webs\WebApp1\WebRoot\mydirectory\foo.txt"
to this:
"/mydirectory/foo.txt"
I'm looking for an existing method like:
public string GetRelativeUrl(string filePath) { }
I can imagine a lot of people having this question... My solution is:
public static string ResolveRelative(string url)
{
var requestUrl = context.Request.Url;
string baseUrl = string.Format("{0}://{1}{2}{3}",
requestUrl.Scheme, requestUrl.Host,
(requestUrl.IsDefaultPort ? "" : ":" + requestUrl.Port),
context.Request.ApplicationPath);
if (toresolve.StartsWith("~"))
{
return baseUrl + toresolve.Substring(1);
}
else
{
return new Uri(new Uri(baseUrl), toresolve).ToString();
}
}
update
Or from filename to virtual path (haven't tested it; you might need some code similar to ResoveRelative above as well... let me know if it works):
public static string GetUrl(string filename)
{
if (filename.StartsWith(context.Request.PhysicalApplicationPath))
{
return context.Request.ApplicationPath +
filename.Substring(context.Request.PhysicalApplicationPath.Length);
}
else
{
throw new ArgumentException("Incorrect physical path");
}
}
try System.Web.Hosting.HostingEnvironment.MapPath method, its static and can be accessed everywhere in web application.
If I have managed to locate and verify the existence of a file using Server.MapPath and I now want to send the user directly to that file, what is the fastest way to convert that absolute path back into a relative web path?
Perhaps this might work:
String RelativePath = AbsolutePath.Replace(Request.ServerVariables["APPL_PHYSICAL_PATH"], String.Empty);
I'm using c# but could be adapted to vb.
Wouldn't it be nice to have Server.RelativePath(path)?
well, you just need to extend it ;-)
public static class ExtensionMethods
{
public static string RelativePath(this HttpServerUtility srv, string path, HttpRequest context)
{
return path.Replace(context.ServerVariables["APPL_PHYSICAL_PATH"], "~/").Replace(#"\", "/");
}
}
With this you can simply call
Server.RelativePath(path, Request);
I know this is old but I needed to account for virtual directories (per #Costo's comment). This seems to help:
static string RelativeFromAbsolutePath(string path)
{
if(HttpContext.Current != null)
{
var request = HttpContext.Current.Request;
var applicationPath = request.PhysicalApplicationPath;
var virtualDir = request.ApplicationPath;
virtualDir = virtualDir == "/" ? virtualDir : (virtualDir + "/");
return path.Replace(applicationPath, virtualDir).Replace(#"\", "/");
}
throw new InvalidOperationException("We can only map an absolute back to a relative path if an HttpContext is available.");
}
I like the idea from Canoas. Unfortunately I had not "HttpContext.Current.Request" available (BundleConfig.cs).
I changed the methode like this:
public static string RelativePath(this HttpServerUtility srv, string path)
{
return path.Replace(HttpContext.Current.Server.MapPath("~/"), "~/").Replace(#"\", "/");
}
If you used Server.MapPath, then you should already have the relative web path. According to the MSDN documentation, this method takes one variable, path, which is the virtual path of the Web server. So if you were able to call the method, you should already have the relative web path immediately accessible.
For asp.net core i wrote helper class to get pathes in both directions.
public class FilePathHelper
{
private readonly IHostingEnvironment _env;
public FilePathHelper(IHostingEnvironment env)
{
_env = env;
}
public string GetVirtualPath(string physicalPath)
{
if (physicalPath == null) throw new ArgumentException("physicalPath is null");
if (!File.Exists(physicalPath)) throw new FileNotFoundException(physicalPath + " doesn't exists");
var lastWord = _env.WebRootPath.Split("\\").Last();
int relativePathIndex = physicalPath.IndexOf(lastWord) + lastWord.Length;
var relativePath = physicalPath.Substring(relativePathIndex);
return $"/{ relativePath.TrimStart('\\').Replace('\\', '/')}";
}
public string GetPhysicalPath(string relativepath)
{
if (relativepath == null) throw new ArgumentException("relativepath is null");
var fileInfo = _env.WebRootFileProvider.GetFileInfo(relativepath);
if (fileInfo.Exists) return fileInfo.PhysicalPath;
else throw new FileNotFoundException("file doesn't exists");
}
from Controller or service inject FilePathHelper and use:
var physicalPath = _fp.GetPhysicalPath("/img/banners/abro.png");
and versa
var virtualPath = _fp.GetVirtualPath(physicalPath);