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.
Related
I'm trying to get the value of what the user has inputted in my textarea on my page and store that value as a string in a variable.
I have spent over an hour doing research on all different ways of getting the value from a textarea in C# and tried many combinations of example code and tried to adapt it to mine but neither of them work. Either the library doesn't exist anymore or something is wrong with example code and I don't want to fix something that is 8+ years old.
Is there any new ways in 2022 to get the value in my razor page textarea and store it in a string so I can re-use it for my needs?
I have seen the post on stack overflow that has been posted over 8+ years and it doesn't work or I'm implementing it wrong.
var mystr = document.getElementById(id).value;
you can put that in a javascript function then gets called on onlcick (like a submit button) and or on the textarea as OnTextChanged.
You have to provide the textarea's Id or use the Html helper method to get it. Here is a working example:
#using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using System;
public class HomeController : Controller
{
IConfiguration Configuration { get; }
public HomeController(IConfiguration configuration) => Configuration = configuration;
public IActionResult Index() => View();
[HttpPost]
public IActionResult Index(string path, string text) // Here you can specify the path of the html file and a text for it in this case will be used to grab the value of an input text area named 'Text'. But it works as well if you want to grab an input from a button and save it as another one in a new html file or somewhere else if you want to do something like that
{
try { var files = Directory.GetFiles(path); } catch (DirectoryNotFoundException) { return View("Error"); } catch (UnauthorizedAccessException) { return View("Error"); } catch (ArgumentNullException) { return View("Error"); } catch (ArgumentException) { return View("Error"); } catch (PathTooLongException) { return View("Error"); } catch (NotSupportedException) { return View("Error"); } catch (SecurityException) { return View("Error"); }
// Here you can get the value of your textarea and save it for further use
var input = Request.Form["Text"]; // This is an example
foreach (var file in files) { // Here you can loop through all the files, edit them or delete them
if (file.Contains(".cshtml")) { // You can just look for a specific file extension here if you want or loop through all the files in your directory, edit or delete any file
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(File.ReadAllText(file));
var formElement = htmlDocument.GetElementbyId("example-form").OuterHtml;
formElement = Regex.Replace(formElement, #"<input name=""Text"" type=""text"" value=""\s*?.*?"" />", "<input name=\"Text\" type=\"text\" value=\"" + input + "\" />");
var streamWriter = new StreamWriter(file); // Replace the content of the file with a string created on the previous line
streamWriter.WriteLineAsync(formElement);
}
}
return View("Index"); // Return your view to be called after saving or editing a file or not depending on what you are trying to do.
}
}
}
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
Background: I'm using the HTML 5 Offline App Cache and dynamically building the manifest file. Basically, the manifest file needs to list each of the static files that your page will request. Works great when the files are actually static, but I'm using Bundling and Minification in System.Web.Optimization, so my files are not static.
When in the DEBUG symbol is loaded (i.e. debugging in VS) then the actual physical files are called from the MVC View. However, when in Release mode, it calls a virtual file that could look something like this: /bundles/scripts/jquery?v=FVs3ACwOLIVInrAl5sdzR2jrCDmVOWFbZMY6g6Q0ulE1
So my question: How can I get that URL in the code to add it to the offline app manifest?
I've tried:
var paths = new List<string>()
{
"~/bundles/styles/common",
"~/bundles/styles/common1024",
"~/bundles/styles/common768",
"~/bundles/styles/common480",
"~/bundles/styles/frontend",
"~/bundles/scripts/jquery",
"~/bundles/scripts/common",
"~/bundles/scripts/frontend"
};
var bundleTable = BundleTable.Bundles;
foreach (var bundle in bundleTable.Where(b => paths.Contains(b.Path)))
{
var bundleContext = new BundleContext(this.HttpContext, bundleTable, bundle.Path);
IEnumerable<BundleFile> files = bundle.GenerateBundleResponse(bundleContext).Files;
foreach (var file in files)
{
var filePath = file.IncludedVirtualPath.TrimStart(new[] { '~' });
sb.AppendFormat(formatFullDomain, filePath);
}
}
As well as replacing GenerateBundleResponse() with EnumerateFiles(), but it just always returns the original file paths.
I'm open to alternative implementation suggestions as well. Thanks.
UPDATE: (7/7/14 13:45)
As well as the answer below I also added this Bundles Registry class to keep a list of the required static files so that it works in debug mode in all browsers. (See comments below)
public class Registry
{
public bool Debug = false;
public Registry()
{
SetDebug();
}
[Conditional("DEBUG")]
private void SetDebug()
{
Debug = true;
}
public IEnumerable<string> CommonScripts
{
get
{
if (Debug)
{
return new string[]{
"/scripts/common/jquery.validate.js",
"/scripts/common/jquery.validate.unobtrusive.js",
"/scripts/common/knockout-3.1.0.debug.js",
"/scripts/common/jquery.timepicker.js",
"/scripts/common/datepicker.js",
"/scripts/common/utils.js",
"/scripts/common/jquery.minicolors.js",
"/scripts/common/chosen.jquery.custom.js"
};
}
else
{
return new string[]{
"/scripts/common/commonbundle.js"
};
}
}
}
}
I'm by no means happy with this solution. Please make suggestions if you can improve on this.
I can suggest an alternative from this blog post create your own token.
In summary the author suggests using web essentials to create the bundled file and then creating a razor helper to generate the token, in this case based on the last changed date and time.
public static class StaticFile
{
public static string Version(string rootRelativePath)
{
if (HttpRuntime.Cache[rootRelativePath] == null)
{
var absolutePath = HostingEnvironment.MapPath(rootRelativePath);
var lastChangedDateTime = File.GetLastWriteTime(absolutePath);
if (rootRelativePath.StartsWith("~"))
{
rootRelativePath = rootRelativePath.Substring(1);
}
var versionedUrl = rootRelativePath + "?v=" + lastChangedDateTime.Ticks;
HttpRuntime.Cache.Insert(rootRelativePath, versionedUrl, new CacheDependency(absolutePath));
}
return HttpRuntime.Cache[rootRelativePath] as string;
}
}
Then you can reference the bundled file like so...
#section scripts {
<script src="#StaticFile.Version("~/Scripts/app/myAppBundle.min.js")"></script>}
Then you have control of the token and can do what you want with it.
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;
}
}
}
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.