File Denial

Introduction

A common web site requirement is the ability to share certain files with specific users. For example,
customers who have purchased software or other digital assets from a web site should ideally be able to return to
that site and re-download the file(s) any time they want. However, that user (and other users) must be
prevented from downloading packages that they have not purchased.

This article will not attempt to cover
ASP.NET user authentication
techniques since that topic is already thoroughly
covered elsewhere. Instead this article will focus on how (and how not) to provide access to
a file once it has been determined the user is indeed authorized to download it.

How Not to Share Files

Many webmasters have a public folder underneath their web root somewhere that holds downloadable files. When
appropriate, the user is provided with a hyperlink to a file within that folder which allows the user to download
it. While this approach is common, it is also very insecure since anyone that knows that URL can download the
file. The only security in place here is "security through obscurity", since it relies completely on users not
knowing information; users that have not been given the URL will not know that they can download it. However, a
user could easily give the URL to other people or post it on the Internet, or a hacker could intercept it by
probing network traffic, etc. Before you know it everybody has the URL and can download at will.

Because of this insecurity, valuable files should never be placed in a public folder, unless perhaps it is an
Intranet application (not available over the Internet) and is secured by Windows Authentication with the
appropriate
ACLs set
on the files.

Private Parts

Instead, files should be stored in a secure location and managed via code to allow downloads only for select
individuals. There are two main accepted techniques for storing files securely in an ASP.NET web
site:

1) Store the files in a private folder that is directly inaccessible to web users. 2) Store the files in a
database (such as SQL Server).

Both techniques have potential performance implications, depending primarily on the number and size of files.
Other factors include the frequency of file updates & uploads, the number of concurrent users, hardware capacity,
bandwidth, etc. So if top performance is a top priority then you should test both techniques to see which is
better for your particular application.

Private Folders

When storing downloadable files on a server's file system, instead of storing them in a public folder (such as
c:\inetpub\wwwroot\myapp\downloads) they should instead be stored in a folder outside the web
application's directory hierarchy (such as d:\downloads).

If it's not possible to store the files outside the application's folder hierarchy (as the case may be if
you're using a shared host) then the download folder should be secured so it cannot be directly accessed via the
Internet. This can be done via IIS (by turning of all permissions off to that folder as shown in Figure 1) or via
Windows ACL settings (as shown in Figure 2) by removing permissions.
Alternately, many shared hosts have custom user interfaces that allow the configuration of such settings.

Figure 2: The standard Windows security dialogs can be used to adjust ACLs and prevent access to
unauthorized user accounts

The Response.WriteFile method can be used to
send a file to the user dynamically from a private folder once it has been determined they are authorized to
download it. Here's an example:

Since this code is sending out a file instead of
HTML, the first line clears the page's Response object to get rid of any
HTML that may already be buffered in the output stream. Then the content type is set on the second
line to inform the browser that the output is (in this case) an Excel file. The
Response.Writefile method is then invoked on the 3rd line to grab the file from the web
server's private folder and output its contents. On the fourth line of the above code sample, a header is added
to the response object to let the browser know that it may open the Excel file embedded within the
browser. (Replace "inline" with "attachment" to specify that it should be opened in an external Excel application
window instead.) Finally, the Response is ended to ensure nothing else is written to the
Response stream.

Note: In some cases you may need to use impersonation or grant access to the ASPNET
user account (aka NetworkService) in order for ASP.NET to be able to access a private
file share.

Database Storage

Applications that require large numbers of downloadable files - or allow users to upload files - may benefit
from storing those files in a database instead of the web server's file system. By leveraging SQL Server's data
management capabilities you can avoid writing reams of custom file management code that may otherwise be
necessary.

SQL Server 2000 can store binary file data in a field of type
image. SQL Server 2005 has improved binary storage capabilities in the form of the new
varbinary data type.

The following block shows VB.NET code that retrieves binary data from a SQL Server query and
sends the resulting file to the user:

Just as in the previous example, the Response object is first cleared of any buffered
HTML since a file is being output instead. Next the content type that was stored along with the file
is retrieved from the DataReader's current row and assigned to the Response's
ContentType property.

The third line is the most important. The file data is written to the Response's output stream
after the binary data is converted into a byte array. The file size that was also stored along with the file data
is cast into an integer and passed as a parameter to the write method.

Finally the file's name is retrieved from the DataReader and provided to the browser via the
Content-Disposition header, and the Response is ended to prevent anything else from
slipping into the output.

Summary

You should now know how to provide ASP.NET file download capabilities to authorized users without
compromising the security of those files. By storing files in a private folder or database, they can be easily
retrieved with a just few lines of code - after you've determined the user is authorized to access the file.

References

For further reading on this subject you may wish to review these articles: