Disallow some file extension from being browsed - asp.net

I m using Asp.net mvc to get my web site and it is used to show some files uploaded by the admin.
There is a directory (Upload) and admin puts the files on it.
Now the thing i want to know is that no one can access the file by just browsing the url below.
'www.mysite.com/Upload/somePdfFiles.pdf'
Now i want to disallow 'pdf' extension to be downloaded.
Alternatively, i m going to design a page and i m going to get the file path by query string,session etc so that i can download the file by myself. In this page i m going to check some privilege.
So how to disallow 'pdf' extension to be browsed.

I don't think that's possible - you can't differentiate between viewing and downloading because ultimately the actual file content is sent down to the browser and that content is accessible by either saving a document, or by directly accessing the HTTP content with an HTTP client or an HTTP Proxy that can capture the downloaded data.
You can deny access to files in a number of ways so that they are not accessible at all, but you can't make a file 'read-only'. To deny access you can disable access to certain extensions using either IIS filtering, explicit Location exclusion (in web.config) or mapping the extension of choice to the HttpForbiddenHandler.
Whether you view a file as a document in browser or downloaded is determined via HTTP headers. If you don't explicitly specify a Content-Disposition: attachment; filename=<filename> in your headers, the browser will try to open the downloaded content inside of the Web browser using appropriate viewer. For PDF this usually means it'll show in the built in PDF viewer or installed PDF extension. But even if people use the viewer they can always save the document from the viewer so you can't make that content read only.

See HttpForbiddenHandler Class and use in your httpHandlers settings.
e.g.
Restrict links/direct requests to PDF files in /files folder only (403)
<system.webServer>
<handlers>
<!-- If you want to restrict all links
<add verb="*" name="RestrictPDFGlobal" path="*.pdf" type="System.Web.HttpForbiddenHandler"/>
-->
<add verb="*" name="RestrictPDF" path="/files/*.pdf" type="System.Web.HttpForbiddenHandler"/>
</handlers>
</system.webServer>
Alternatively, i m going to design a page and i m going to get the
file path by query string,session etc so that i can download the file
by myself. In this page i m going to check some privilege.
You could do that (interesting note below) by privileged access - e.g. [Authorize] attribute
Sample, improve as needed:
in Home Controller:
[Authorize]
public FilePathResult DownloadPdf()
{
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "foo.pdf"
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File("~/files/foo.pdf", "application/pdf");
}
View:
<p>#Html.ActionLink("PDF Download", "DownloadPdf", "Home")</p>
Note:
The interesting thing here (I don't know the answer), is why the restriction set in config "allows" this approach (where we "manually" return it via Controller -> Action vs a "direct" request like in a link).
Hth

Related

Path traversal vulnerability

The concept of path traversal is new to me need some guidance please.
In my project I have following line of code:
uploadimg.SaveAs(Server.MapPath("tempfiles/" + fUIName));
FileUpload1.SaveAs(Server.MapPath("tempfiles/" + fSIName));
Is this code is vulnerable to PathTraversal vulnerability.
Can any one help me understanding the concept of path traversal and how to remove/avoid it.
Thanks!
Edit 1:
It is also mentioned that I am storing files in tempfiles folder temporary. After the purpose of saving the file fulfilled I am deleting the files from tempfiles. So can I skip this vulnerability?
Please guide.
Thanks!
The path traversal is means that some one upload a file to your site and can access it direct from the URL (if he knows the path, or can find it from some other page).
Eg, lets say that you upload a pdf file named file.pdf at tempfiles/
Then you probably show it on some page as http://example.com/tempfiles/file.pdf
Now the attacker knows where the file is uploaded, and then its upload to you some other file, maybe an html with fraud, maybe some server browser in an aspx page etc... and direct call it from the url.
Solutions
You can upload all the files to a secure folder like App_Data that you can not direct access it.
You can upload it to a folder that you change the permissions and again you can not direct access it. (see here how you can do that How to set correct file permissions for ASP.NET on IIS)
You can limit the extensions for what you upload and let only images for example, and put that on that directory to avoid anyone to run anything there.
<configuration>
<system.web>
<authorization>
<deny users="*" />
</authorization>
</system.web>
</configuration>
Now, if you upload pdf to a directory that the user can not access direct from the url, you need to create a handler that return the uploaded files. The handler must knows if the user is allowed to view the file, if the file is safe, if the file come direct from the site.
some simple examples.
file download by calling .ashx page and Alternate image display in asp.net
And one last solution is to check the reference and make sure that is comming from your site and its not a direct call from the url using this HttpContext.Current.Request.UrlReferrer.Host. Meaning that the user is uploading an image, but its allowed to view it only if its come the request from a page of your site using some link.

How to configure handler path to serve images from folder and database?

I am using a handler to serve images from database. I have a problem with the path configured for the handler.
<add name="DbFileHandler" verb="*" path="/images/db/*" type="DbFileHandler"
resourceType="Unspecified" allowPathInfo="true" />
The flow we want to implement is:
1) First Image is requested from a folder say "/images/db".
2) If the folder doesn't contains the image, the handler is called which fetches the image form the Db, displays it and writes the image to the folder
Also we have implemeted Imageresizer library to get images.
The problem here is when we request:
http://www.abc.com/images/db/101 -- Handler is called
http://www.abc.com/images/db/image.jpg?width=200 -- Image from folder is called
http://www.abc.com/images/db/image.jpg -- Again Handler is called instead of calling the Image from folder
How can we configure the handler so that a request to http://www.abc.com/images/db/image.jpg is not directed to the handler and instead served from "/images/db/" folder?
N.B: I would like to keep the path same i.e http://www.abc.com/images/db/
Are you using IIS express/full? If so, IIS is probably handling the .jpg extension as a static file and bypassing your handler. You need to set:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
This will ensure that IIS won't treat it as a static file and that your handler will run.
You'll then need to do the check manually to see if the image is there and serve that disk image if so, which is what IIS would do, but IIS would return a 404 if it wasn't found whereas you need to do your image processing and return the image data.
Please read the ImageResizer Best Practices.
Using ImageResizer properly, you only need to use DiskCache and the SqlReader plugin (Or build your own provider), and ImageResizer will handle request interception and (correctly threaded) caching for you.
Aside from the inherent performance limitations of using a HttpHandler instead of an HttpModule, under load you will create a lot of locked and/or corrupted files unless you implement a threading system that can coordinate the disk I/O.
I would strongly urge you to re-consider the approach you are taking here.

Force file download in a browser using ASP.Net MVC when the file is located on a different server without downloading it on my server first

Here's what I would like to accomplish:
I have a file stored in Windows Azure Blob Storage (or for that matter any file which is not on my web server but accessible via a URL).
I want to force download a file without actually downloading the file on my web server first i.e. browser should automatically fetch the file from this external URL and prompts the user to download it.
Possible Solutions Explored:
Here's what I have explored so far (and why they won't work):
Using something like FileContentResult as described here Returning a file to View/Download in ASP.NET MVC to download the file. This solution would require me to fetch the contents on my server and then stream from my server to the browser. For this reason this solution won't work.
Using HTML 5 download attribute: HTML 5 download attribute would have worked perfectly fine however the problem is that while it is really a very neat solution, it is not supported in all browsers.
Changing the file's content type: Another thing I could do (at least for the files that I own) to change the content type property of the file to something that the browser wouldn't understand and thus would be forced to download the file. This might work in some browsers however not in all as IE is smart enough to go beyond the content type and sees the file's content to determine the content type. Furthermore if I don't own the files, then I won't have access to changing the content type of the file.
Simply put, in my controller action I should be able to specify the URL of the file and somehow browser should force download the file.
Is this something which can be accomplished? If yes, then any ideas how I could accomplish this?
Simply put, in my controller action I should be able to specify the URL of the file and somehow browser should force download the file [without exposing the URL of the file to the client].
You can't. If the final URL is to remain hidden, your server must serve the data, so your server must download the file from the URL.
Your client can't download a file it can't get the URL to.
You can create file transfer WCF service (REST) which will stream your content from blob storage or from other sources through your file managers to client browser directly by URL.
https://{service}/FileTransfer/DownloadFile/{id, synonym, filename etc}
Blob path won't be exposed, web application will be free from file transfer issues.

Preventing Users from Downloading file from website directly, how?

I need very advanced and effective method of Preventing Users from Downloading file from website directly in asp.net.
Method should -
limit downloads,
limit time,
limit requests,
etc.
but should be downloadable by active login users
Delete the file from the server.
Any user trying to download it will not longer succeed.
You can put your files into a directory and configure that directory as not accessible by public users.
Store the files in a folder which is not accessible via IIS (i.e., not underneath your web application's root)
Create an .ashx generic handler which takes a file identifier (either filename, or ID of some sort) as a QueryString parameter.
In that .ashx, perform whatever checks you want to perform: is the user logged in? have they downloaded too many files? etc.
Then, if you decide that they should be allowed to download it, set the response headers appropriately and write the file out to Response.OutputStream

Uploading files to my site

I have an ASP.NET application. I want users to be able to upload documents. Where in the file system should I store those documents? Users should be able to upload them and see the hyperlinks to them on the site, but UserA should not be able to see UserB's documents, but the administrator role should be able to see all of them.
I'm assuming I don't want to upload them to a folder with my web application because then the web server can serve them up directly. I don't want to store the file in the database, but I can store file paths in the database.
Somebody please give me some best practices. Thanks!
Depending on the size of the files, one options would be to store the files outside the web root so no one could hot-link to them, then, as has been suggested, produce a page that takes some arguments and Response.WriteFile() from said directory.
If the files are large you might want to use Response.TransmitFile to save on some memory on the server.
From an implemenetation point of view, I would probably store the real name of the file in the database to avoid naming collisions and save the files on disk renamed to something like a GUID or just an integer ID taken from the database table.
Then when you write the file to the output stream you can use a content disposition header to rename the file back to the original name.
HTH!
You probably want a folder structure with one folder per user. You could write your own CreateFolder() method that after creating the folder adds a web.config file with authorization rules, letting only the user and administrators access it. That way, users can only access their own files, but admins can access all.
EDIT: For clarification - I am assuming that you are using the ASP.NET Membership features (although not necessarily exactly as is), which lets you put individual web.config files in a directory with the nodes shown below to control access.
<configuration>
<system.web>
<authorization>
<allow roles="admin" />
<allow users="TomasLycken" />
<deny users="*" />
</authorization>
</system.web>
</configuration>
Another way of doing it, is to place all the files in one place, to which you statically allow no access to anyone, and then have a generic handler (.ashx) which serves the files after looking up url and permissions from the database. This is probably a cleaner approach, and it will also take less space seeing as you won't have tons of web.config files laying around...
Note: I purposely posted this as a separate answer, so you can mark the solution you prefer as your answer and ignore the other one.
You can create a virtual directory adjacent (as a sibling) to the root of your website in IIS this does a couple of things 1) it prevents users from accessing the files directly by guessing the file location, 2) you can point the virtual directory to any location you wish, 3) with directory browsing turned off you can prevent anyone from seeing the structure. You can then store the paths in a database further removing the actual structure from the clients.
I have implemented solution like this. I store files in the protected (via web.config section) folder within the website, but instead of real file names I used guids. I also had a database that mapped guids to real names and had some extra information like file size, type etc. In your case this information could also contain name of the user that uploaded the file.
To download the file I implemented HTTP handler that would get a guid parameter and based on the file type set appropriate HTTP header and write the content of the file into response. Before I write file to the response I also check permissions for the current user. Then I have a page that render a list of file names as hyperlinks that point to this HTTP handler with guid parameter that correspond to particular file in the protected folder.
For upload I used very nice SlickUpload control:
http://krystalware.com/Products/SlickUpload/

Resources