Why can't I access static files that I add programmatically from a POST request?

Viewed 93

I'm attempting to make a basic .NET API for managing a collection of media (images and videos).

I have configured the webroot to be a folder called "site", and within that folder is a folder called "media" where these files are stored. I can access a test media file that is saved in /site/media/Smush.jpg by loading https://localhost:5001/site/media/smush.jpg - this serves the image as expected.

I have created a method that receives a POST request containing form data from my frontend, and this method saves the file to the webroot using a filestream, code below:

[HttpPost]
        [Route("/media/add")]
        public async Task<HttpResponseMessage> MediaAdd()
        {
            try
            {
                //get the form
                var form = HttpContext.Request.Form;

                //if there's a route, add it into the filepath, otherwise leave it out and have the filepath go straight to media (this prevents an exception if route is blank)
                string filePath = form["route"] == "" ? Path.Combine(_hostingEnvironment.WebRootPath, "media") : Path.Combine(_hostingEnvironment.WebRootPath, "media", form["route"]);
                
                //get the first (should be only) image - DO WE WANT TO BE ABLE TO ADD MULTIPLE IMAGES? PROBABLY TBH
                IFormFile image = form.Files.First();

                if (image.Length > 0)
                {
                    //check the directory exists - create it if not
                    if (!Directory.Exists(filePath)) {
                        Directory.CreateDirectory(filePath);
                    }

                    using (Stream fileStream = new FileStream(Path.Combine(filePath, form["filename"]), FileMode.Create))
                    {
                        await image.CopyToAsync(fileStream);
                        return new HttpResponseMessage(HttpStatusCode.OK);                        
                    }
                }
                else {
                    return new HttpResponseMessage(HttpStatusCode.BadRequest);
                }                
            }
            catch (Exception e)
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }
        }

My frontend submits a route, filename and the media file, and this is used to save the image. This all works fine; I can submit an image with the path "test" and the name "test.jpg", and the API correctly stores the file at /site/media/test/test.jpg. I can view the file in the solution and see a preview of the image, as with Smush.jpg.

Demonstration that the test file is added to the APIs local files correctly

However, attempting to load https://localhost:5001/site/media/test/test.jpg results in a 404. Why is this the case? Can I not add files into the webroot through code and have them be accessible as static files as if I added them to the solution in my IDE? Are there any alternative ways of handling this?

I am using .NET 5.0, and have app.UseStaticFiles(); in Configure() in Startup.cs.

Sorry if this is a duplicate, but I couldn't find anything else like this.

EDIT: On checking things again, it seems like rather than my files being at https://localhost:5001/site/media, they are simply in https://localhost:5001/media. I am not sure how I was able to access Smush.jpg at https://localhost:5001/site/media/Smush.jpg before.

It seems as though the webroot is not included as part of a URL to access files within it.

As it is now, I have got what I was looking for it to do.

2 Answers

Well first a security concern as also @Heinzi pointed out...

string filePath = form["route"] == "" ? Path.Combine(_hostingEnvironment.WebRootPath, "media") : Path.Combine(_hostingEnvironment.WebRootPath, "media", form["route"]);

What if the user sends form.route == "../../" and instead of image he updates the appsettings.json file ?

Check this out and have that in mind if you're planing to release this code to a production environment and make sure you only accept image files.

On the other hand if you are serving static files from a folder different to wwwroot please use this configuration

Why the 404

It makes sense. You are under the controller/action paths. Going under the site url the engine does the following:

When you request https://localhost:5001/site/media/test/test.jpg the code tries to find the media controller and the test action. It is not looking for static files on the filesystem. Since there is no such controller/action pairs, it will not find anything and thus return 404 not found.

If you saved the files in https://localhost:5001/static/media/test.jpg outside of the mapped routes, you would be able to access it.

Look inside your code for:

MapHttpRoute

Which is used to configure how to identify controller actions which are not decorated with the [Route] attribute.

Security concern

When you want to upload a file, you should consider a better solution and not one that accesses directly your filesystem.

Possible options:

  • Blob storage on the cloud
  • Database blobs

Don't forget to sanitize the input with an antivirs or some similar solution.

Related