Is it safe to map a virtual directory to a subfolder within App_Data for serving images? - asp.net

I am running an mvc3 app under IIS7.
the general public can upload images via a file upload control, which resizes the images then writes them to a subfolder of the App_Data folder.
When it comes to displaying the uploaded images on pages, I cannot serve these straight from the App_Data subfolder because this is restricted by IIS7 as a protected folder for security reasons.
If i map a virtual directory to the App_Data subfolder i can serve the images stored in there, but this raises the question - does this pose a security risk?
From what i understand, as long as the correct permissions are set on the virtual directory, then it should be ok.
Is this correct? or are there other things i am overlooking?

From what i understand, as long as the correct permissions are set on the virtual directory, then it should be ok.
That's correct. If you ensure that only authorized users can access this virtual directory your files are pretty safe.
But to avoid all the hassle of creating another virtual directory you could create a controller action which will directly serve files from this folder:
// You could specify Roles instead of Users if you wish
[Authorize(Users = "john, jane")]
public ActionResult AppDataFile(string filename)
{
var file = Path.Combine(Server.MapPath("~/app_data"), filename);
if (!System.IO.File.Exists(file))
{
return HttpNotFound();
}
return File(file, "application/octet-stream", Path.GetFileName(file));
}
and then when you authenticate as jane you could /appdatafile?filename=foo.bar to download the foo.bar file from the App_Data folder.

Related

Where to upload non-public user files in Symfony

I am working on a Symfony 2.8 project, and I would like to ask users to upload files via a form. These files are not public (like a profile avatar or something), so I don't want to place them under web/, after uploading them only the admins are allowed to know the location of them and to open them via the admin interface of the site. Moving uploaded files near the source code in the actual bundle sounds wrong to me...so is there any official or recommended path to move the uploaded files which is still under the root of the project?
If your requirement is that uploaded files should be downloadable by authorized people only then you must keep them outside the web root obviously. Any directory outside would be fine however they should be kept outside your codebase as well.
You could create data directory in the project root for instance. You can event mount a NFS/S3 share, that choice is yours to make.
To allow files to be downloaded you can create a controller which would serve files to authorized requests only. Example controller (PHP7 based):
final class DownloadController extends Controller
{
/**
* #Route("/download/{path}", requirements={"path"=".+"}, name="download")
* #Method("GET")
* #Security("is_granted('FILE_DOWNLOAD', path)")
*/
public function __invoke(string $path): Response
{
return $this->file($yourUploadBasePath . $path);
}
}
You can either create a data or uploads directory in your project root or if your server is nginx and you have control over web server configuration, you could use http_auth_request module:
https://nginx.org/en/docs/http/ngx_http_auth_request_module.html
Then if you try to access the file, nginx performs a subrequest to your application and if your app returns anything else than 2xx status code, access to the file is denied.
The downside of this approach is that if you replace the webserver or have some error in the configuration, those files would be publicly exposed.

Hide/encrypt or otherwise change path to mp4 file in drupal

I have video files (mp4's as I want people to be able to view them on ipads etc.) that I serve to users. However some of these videos are only available to users who have a certain number of user points. I have that working in that if a user doesn't have enough user points they can't view the node. All users have the same role (video viewer) and the problem is that it is possible for someone who has enough user points to view the node, grab the url of the video and then give it to someone who has the video viewer role but doesn't have enough user points and then that person can directly download that mp4.
Just looking for a way to limit access to the mp4 file if a user does not have access to the node or hide the path to the file somehow. I have the mp4's stored in a private file system but this hasn't solved the problem as the users have the same role.
I've got this (http://www.ioncube.com/html_encoder.php) working on static pages in my webspace (non drupal pages) but can't get it working in my drupal setup. When I include the php code in my node to include the php file it just gives me a blank page.
Many thanks
There's not much I could say about this that the Drupal documentation doesn't already.
http://drupal.org/documentation/modules/file#access
Managing file locations and access
When you create a file field, you can specify the sub-directory of the site's file system where uploaded files for this content type will be stored. The site's file system paths are defined on the File system page (Administer > Configuration > Media: File system).
You can also specify whether files are stored in a public directory or in a private file storage area. Files in the public directory can be accessed directly through the web server; when public files are listed, direct links to the files are used and anyone who knows a file's URL can download the file. Files in the private directory are not accessible directly through the web server; when private files are listed, the links are Drupal path requests. This adds to server load and download time, since Drupal must resolve the path for each file download request, but allows for access restrictions.
The best practice for public files is to store them in the multi-site directory like: sites/default/files
The default way to securely add a private directory for your files is to use a directory that can not be accessed directly by your web server, but can be accessed by Drupal. Ideally this directory should be located outside of your Drupal root folder.
The simple way to add a private directory for your files is to create a sub-directory under the public directory like: sites/default/files/private
When you specify the private directory in admin/config/media/file-system it will automatically create the sub-directory & create a simple .htaccess file with Deny from all. This stops Apache from serving files from this directory. Make sure that you test this by adding file to that directory and verifying that you can't browse there directly. If this isn't working, all files in this directory will be accessible to anyone who can guess the URL! Note that non-Apache web servers may need additional configuration to secure private file directories.
Accessing Private Files
Once configured, files stored in the private directory are inaccessible via a direct link; however, if Drupal constructs a link to the file, the file will be accessible to anyone who can see the link.
For example: you have created a new content type with a file field which stores files in your site's private file directory. Next you create a node from this new content type and attach two new files. When the node is published links to both attached files are visible and anyone who can view the node may download the files. Now, if you unpublish the node, all attached files become inaccessible for download even if you use the direct link to the files that worked when the node was published.
Re-publish the node, and disable the "display" checkbox for one of the files. Save the node. Now one file is accessible for public download and the other is not accessible--even if you have the direct URL for the file that is not listed you will not be able to download this file.
For finer grained control of who can see/download attached files you will need an additional access control module. You may write a module yourself, or use a contributed module such as Content Access.

Read Write Permissions on IIS

I have create a directory uploads in the Content directory and inside the uploads dir i create a directory at the time of creation of user account. This setup works fine on the development setup but when i publish the site on my local IIS i get a access denied error.
Access to the path 'C:\inetpub\wwwroot\Content\uploads/d1bed3b7-f9e1-486a-953a-37abc7b99dad' is denied.
I have a shared hosting environment, the question i want to ask is, will i be able to set the permissions on the shared hosting environment?
What are the alternatives?
EDIT
here is code(tnx to #Marko) for creating the directory for the user
string UserGUID = Guid.NewGuid().ToString();
string path = HostingEnvironment.MapPath(#"~/Content/uploads");
Directory.CreateDirectory(path+"/" + UserGUID);
EDIT
after changing the code the error message has improved
Access to the path 'C:\inetpub\wwwroot\Content\uploads\ab569312-c487-4a08-bf49-99f7c69303f6' is denied.
There's a problem with the path, you have the wrong slash /
C:\inetpub\wwwroot\Content\uploads--------->/<----------d1bed3b7-f9e1-486a-953a-37abc7b99dad'
As long as the \uploads\ path has writable permissions any folders/files you create within that folder should inherit permissions from the parent.
EDIT
I'm not 100% sure why it works in cassini but change your code to this (it uses Path.Combine) instead of adding a slash manually (which is actually the wrong slash).
string UserGUID = Guid.NewGuid().ToString();
string path = HostingEnvironment.MapPath(#"~/Content/uploads");
string userPath = Path.Combine(path, UserGuid);
Directory.CreateDirectory(userPath);
If all of this fails, contact your hosting provider to set writable permissions to the /uploads/ folder or use the Administration panel if you have one.

Save file in a folder above the site files

My host has the following structure:
/Web -> Where is the content of the site
/Data -> Folder permissions to read and write
How do I upload a file to the Data folder?
The code below does not work, since "~" returns the directory / web.
//Save Image
var serverPath = Server.MapPath(Href("~/Data/") + id);
Directory.CreateDirectory(serverPath);
imgOri.Save(Path.Combine(serverPath, fileName));
Server.MapPath() is designed to map a path up to the root directory of the application. As you are trying to upload a file above the root it won't work.
You can upload a file above the root by specifying the exact file path (if the host can provide it):
var serverPath = "C:\YourFolder\Data\") + id);
I'm surprised your host is allowing you to upload a file outside of the root directory as there are a number of dangers in doing this...you may also run into Trust issues.
You can obtain the path to a directory which sits at the same level of your site root by using Server.MapPath as below:
#{
var root = Server.MapPath(".");
var temp = root.Split('\\');
temp[temp.Length - 1] = "Data";
var newpath = string.Join("\\", temp);
}
Hosting companies used to provide "data" directories outside of the root folder as a safe place for things like Access mdb databases. You cannot directly browse to a directory which is outside of the root of your site. ASP.NET did away with the need for these things with the introduction of App_Data. The only reason you would want to use this kind of folder nowadays is if you want to apply some kind of authentication prior to serving the contents of the directory. Then you need to use a handler, or a simple cshtml file will do. You can combine the WebSecurity helper with the WebImage helper to first authenticate the user, and then retrieve and display the image if they pass the test. The src in your img tag will point to the cshtml file, with a querystring or UrlData value so you know which image to display.
If you don't need to validate users prior to displaying image, storing the image files outside of the root adds an unnecessary level of complication.

protecting files with asp.net (mvc)

I want to protect the files in a specific directory so that people cannot link to them - but I still need my program to be capable of rendering them, so password protecting the actual folder won't work. Is there any way to facilitate this in ASP.NET MVC? Obviously it's more about ASP.NET, not MVC - but I can't make use of things like Session State and Postback and such...
You could put that directory outside of the web app's root directory (so that it can't be accessed using some copied URL) or into a directory where you deny any read access using a web.config file.
Then access the files through an action which requires the current user to be authorized, e.g:
public class FileController : Controller
{
[Authorize]
public ActionResult Get(string file)
{
return new File(Path.Combine(_rootPath, file);
}
}
Then you can access the files through an action URL, e.g. http://server/app/File/Get/test.txt.

Resources