ASP.Net Core static file security (images specific

2019-07-18 06:11发布

问题:

I have an issue that seems like a very common requirement, but I'm unable to find any help. Let's say I have an authenticated user uploading private photos to non browsable folder on my server. Each user has their own folder in a large file store, like...

/FileStore/{UserId}/Photos/my_cute_cat.jpg

The file is uploaded and I save a thumbnail of the photo like...

/FileStore/{UserId}/Photos/Thumbs/my_cute_cat_thumb.jpg

That user wants to download their photo. No problem...

  • User sends a request to download
  • I authorize the user and make sure they own that particular photo
  • I serve the file

I need display the thumbnail in a plain old img tag on the user's dashboard. The /Thumbs/ folder is not set up to serve static images. I don't want the /thumbs/ folder to be able to serve static images because they should only be visible to authorized users. What am I supposed to do?

回答1:

If its just a small thumb nail, consider using embedded base64 image with more details here: How to display Base64 images in HTML?

You can pass base64 down to the View by encoding the file into a base 64 format as a string explained here: http://www.devcurry.com/2009/01/convert-string-to-base64-and-base64-to.html

Using this approach or even using a FileActionResult to serve the file through a controller has the big disadvantage of not being able to use a CDN to deliver the cached content. What you can do to help with this is still serve the images statically but give them obscenely long random names which is unguessable. When someone requests the image from you, then you simply provide them with the unguessable url.



回答2:

First and foremost, if a static file should not be available to the whole world, then your web server should not serve the directory it is in at all. Nothing else will do on that front. If the directory is served, then the image can leak.

That then presents the problem of how to allow the user to access it. The simple answer there is that you need an authorized action that returns the image(s). Basically, that's going to look something like:

[Authorize]
public async IActionResult Image(string image)
{
    var file = $"/FileStore/{User.Identity.GetUserId()}/Photos/{image}";
    if (!File.Exists(file))
        return NotFound();

    return File(file);
}

Now, obviously, I don't know about your "FileStore", so the exact code here may need to change. The general idea is that you simply see if this file exists under the user's directory. If it does, they can have it. If not, they don't own it. You also should probably add in a bit more security, such as restricting the image param to only image types, so someone can't try to pull just any arbitrary file. They'd still have to somehow manage to get some aberrant file there, in the first place, but just in case it's better to not take chances.

The return methodology may need to change as well. Right now, I'm assuming a file on the filesystem of the server, so I just return a FileResult with the path. If you're pulling it from some sort of storage account in Azure, AWS, etc. then you'd use HttpClient to make the request, and then you should stream the response from that directly to a FileStreamResult.



回答3:

I've not tested on linux, but if you make a folder where you have you pictures, you can actually just create a controller method that returns a file. Now, even if you are using a view served from another method, you can still call this file method from this razor view and display the image.

in controller called App I serve the image from a folder called Documents:

public IActionResult File(string id)
    {        
        if (String.IsNullOrEmpty(id))
        {
            return PhysicalFile(Path.Combine(_env.ContentRootPath, "Documents", "Wrong.png"), "image/jpg");
        }

        return PhysicalFile(Path.Combine(_env.ContentRootPath, "Documents", id), "image/jpg");
    }

in razor (using a bit bootstrap):

 <img src="~/App/File/@profilePicture" class="img-fluid" />

use the location of your method that serves the file.