Click here to Skip to main content
Email Password   helpLost your password?

Introduction

If you've developed any kind of e-commerce system that offers downloadable files, you've faced this problem. How do I protect my files from theft? Very often, the problem doesn't even concern payment. Many web-sites offer useful files for free. All they ask in return is that the person downloading the file simply enters some personal details, or fills out a form for the administrator's records. All too often, people don't even want to do that and they seek ways to circumvent the web server's security.

The key problem in this scenario is the fact that, no matter which way you look at it, ultimately, you have to offer users a link to the downloadable file. Once someone has that link, they can offer it to their friends and colleagues, who then simply circumvent all of your security features and download the file directly. None of the security features you put in place are worth anything if a user knows that final link to the downloadable file itself. I've seen sites that go to great lengths to "hide" these links (including masking the link by writing specific text in the browser's status bar when a user hovers over it). A simple "View Source" circumvents all these security measures.

The problem lies in the fact that your downloadable file (whether it be a .pdf, .doc, or .zip - or even a .exe) has no way to decide whether it should serve itself or not. You can't place logic in these files to decide whether or not to serve them.

One alternative is to place system security on the folder in which these files reside. This is not really viable though, as you would need to create a new system (Windows) account for each registration, send the person their username & password, and then remove their user account from the system once they have downloaded the file. This makes for an administration nightmare (not to mention an even bigger system security issue). And if you're hosting your site with an external ISP, it's highly unlikely that they'll allow you to create and delete user accounts on their server at will.

The simple solution below closes the above security hole by storing the downloadable file outside a web-site and then serving the file directly from a web page. In other words, you might link to MyFileServer.aspx and receive MyPdfDownload.pdf, for example - with no actual link to MyPdfDownload.pdf offered anywhere on your web-site.

Using the code

The code is fairly straightforward. What we're going to do is open a file stream and serve that instead of the usual text content. In other words, our .aspx page is going to behave more like a file-download. I'm using VB.NET, but this can just as easily be done using C#.

My code below assumes that you've created a folder called C:\My External\File Path\. Note, this folder exists in the C:\ drive and doesn't appear anywhere near C:\Inetpub\wwwroot or any other folder that serves as a web-site on the server. I'm assuming a file called MyPdfDownload.pdf exists in the above folder. You can add any file/ path you choose. Simply update the variables in the appropriate line of code below.

To design the application, first create a folder called C:\My External\File Path\. Then find a .pdf file (you can select any downloadable (.zip, .exe, .doc, .xls etc.) and store it in the above directory. Name it MyPdfDownload.pdf, or change the code below to reflect the file name you've saved in the above folder..

Now check that the directory has appropriate access for the ASP.NET account:

Good. Now you have a folder outside the web. Now create a new ASP.NET web application in Visual Studio .NET.

Add a new Web Form and call it MyFileServer.aspx. Double-click on the page. This will open the code-behind (MyFileServer.aspx.vb) file and create your Page_Load event handler.

We're going to place our code inside this Page_Load() subroutine. This means that every time users link to our page, it will validate them and decide whether to download our external page or not. Think of this as a delegate function, or an alias for the actual file we want to download. The only difference is that now we can put server-side logic in place to decide whether or not we are prepared to serve the file. In other words, we're wrapping some intelligence around our downloadable file that verifies whether or not it should download itself.

Let's start coding.

' First, we need to declare our variables


' We'll need a filestream object to fetch the pdf file from its location

' outside the web-site.

Dim fs As FileStream

' As we're not serving standard html,

' we'll need to tell the browser what type of content we're serving

' This value will need to be added to our response header.

Dim strContentType As String

' This is the path to the folder where our file will be kept.

' Note, we DON'T use the 

' Server.Mappath() method, as this would defeat the object.

' We want to keep the file outside the web application, so

' we can't use standard server object methods.

' In a real-world application you would store this value in a database

' or otherwise in web.config so that you can change it easily

' without having to recompile your code. I've hard-coded it here

' for readability. Note, you will have to ensure that the ASP.NET

' user account on your system has appropriate access rights to this folder

Dim strPath = "C:\My External\File Path\"

' We need to use this file name
' in the headers that we send to the browser. Obviously
' we don't want to show the user the full path
' to our file on the server,
' so I'm not including this in the path variable above.
' Even though they won't be able to access
' it without admin rights on your server, it will only confuse them.
' Users only need to see the file name,
' which they can then save on their own system once downloaded
Dim strFileName As String = "MyPdfDownload.pdf"

' Next we call our validation function to determine
' whether or not we are prepared to offer the user this file
' I'm not including that function in this article.
' You can create your own validation
' function based on your site's requirements
' You can even include code in this page to indicate
' whether a user has downloaded their file.
' Then save a cookie on their machine and make
' a note in your database. In any future attempts,
' you can check for the cookie to ensure that the user can ONLY download
' the file to the same machine. That way you can prevent
' them from giving their login details to others who could then 
' download the file from their machines.
If UserIsValid() Then

   ' Great. The user is valid and we're happy to give
   ' them the file. Now we open our file using the file stream object
   fs = File.Open(strPath & strFileName, FileMode.Open)

   ' Declare a byte array to hold our stream information.
   ' Note, we initialize the byte array to the file stream's size
   Dim bytBytes(fs.Length) As Byte

   ' Write the stream to the byte array
   fs.Read(bytBytes, 0, fs.Length)

   ' Close the file stream to release the resource
   ' This is important. If you don't close the resource,
   ' the next person trying to download your file
   ' may get an error saying the resource
   ' is still being used by another application.   
   fs.Close()
   
   ' Next we need to add some header information.
   ' These headers will tell the browser what it needs to do
   ' with the content we're serving

   ' The first header ensures that the file name
   ' is correct on the client side. This is extremely important! If you
   ' don't add this header, the browser will make
   ' some sort of arbitrary decision as to what the file should be called.
   ' It will either offer no name and force the user to select a name
   ' (As we all know, this is like giving a two-year-old a loaded gun)
   ' or it will simply name the file after the web page
   ' it's calling (eg. MyFileServer.aspx). This will save without any problem
   ' but when the user tries to open the file, their system
   ' will tell them it doesn't know what to do with a *.aspx file
   ' and you're going to slowly go insane over the next
   ' six months with support calls, trying to explain to users how to
   ' change the file extension from *.aspx to *.pdf on their system
   Response.AddHeader("Content-disposition", _
              "attachment; filename=" & strFileName)

   ' Next, we need to tell the browser what type of content
   ' we're serving. If we don't add this header, the user's browser
   ' will assume it is standard html and try to render your
   ' bytes as text. The page won't crash,
   ' but the user is going to see an unholy
   ' mess on their web page, with bundles of little blocks
   ' and funny faces that are absolutely meaningless to them.
   ' I'm using a standard application/octet-stream content type.
   ' It's better to find out exactly what MIME type your particular
   ' file extension is defined under, as this should produce
   ' better browser behaviour.
   ' However, for our purposes, this will work just fine
   ' This header tells the browser that it is serving
   ' an application file as a byte stream.
   ' The browser will know immediately
   ' that it shouldn't serve this file as text and will open
   ' the File Download box instead,
   ' in which it offers the user the ability to save
   ' the file. It will also use the Content-disposition header
   ' (see above) to auto-populate
   ' the Save File dialog box with the file's name
   Response.ContentType = "application/octet-stream"

   ' Now our headers are added, we can serve the content.
   ' To do this, we use the BinaryWrite() method of the server object
   ' This successfully streams our external file to the user,
   ' despite the fact that the file doesn't exist
   ' anywhere inside the web application
   Response.BinaryWrite(bytBytes)

   ' Call Response.End() so that no more
   ' content goes through to the client.
   ' The file has been downloaded,
   ' but if this method is not called, the page
   ' will continue downloading any remaining
   ' html/ text content and mess up the resulting stream.
   ' This method call ensures that the downloaded
   ' file doesn't end up corrupted with unwanted data   
   Response.End()
Else

   ' The validation function returned false.
   ' In other words, we don't want to allow this user access.
   ' Simply serve the user normal text/html
   ' informing them that they don't have access to the file
   Response.Write("Sorry. You don't have access" & _ 

     " to this file. Please return to the login page and try again.")
End If

Points of Interest

XP has some new security settings that may cause problems on the file download feature in browsers. You'll probably need some instructions for people with XP systems to follow. This is a short-term thing - and it's something they'll experience on any file download over the web. As users become accustomed to XP's settings and defaults, they should begin placing fewer support calls.

As a rule, I post the following under the FAQs of any site that uses the above code:

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralSave download count
gogetsome
6:13 31 Aug '09  
Hello, great solution! I would like to update a download count for the particular file being downloaded. I need to execute an update procedure if the user clicks the save button. How can this be accomplished???
GeneralBuffered File Donwload
codemobile
20:47 29 Nov '08  
Visit for Buffered file download in asp.net

http://developerassistant.blogspot.com/2008/11/buffered-file-download-from-aspnet-web.html[^]
Generalgreat article!!!!
isaias
22:32 20 Mar '08  
excelent example!!!
thank you for help me
it's very clear Smile
Generalit Work for me
m-chaos
0:30 27 Feb '08  
Thank you Big Grin
Generalthanks [modified]
M.S. Babaei
19:24 15 Nov '07  
it's really great

and very simple

simply I changed it to perl

thanks again for understandability



with Best Regards
M.S. Babaei

-- modified at 5:34 Saturday 17th November, 2007
GeneralHow to download Video file like mpeg
Tejas G. Patel
2:56 21 Oct '07  
Hi

Wht should be code to prompt user to download any video file which is reside on server.I have tried tag with imageURL set to the location of my video file and it does open as soon as user click the link.but i want it to ask user for download and dont want to start video when user click for download.

Please help me as soon as possible

Thanx in advance.

Bye
GeneralASP.NET is not authorized to access the requested resource
Yabsley
12:07 12 Sep '07  
Hi, can anyone offer me some advice please? I've implemented the code, created a directory outside the bounds of my website and give \ASPNET full rights to it. But I get this message:

Access to the path 'c:\rts_data\files\event_4' is denied.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.UnauthorizedAccessException: Access to the path 'c:\rts_data\files\event_4' is denied.

ASP.NET is not authorized to access the requested resource. Consider granting access rights to the resource to the ASP.NET request identity. ASP.NET has a base process identity (typically {MACHINE}\ASPNET on IIS 5 or Network Service on IIS 6) that is used if the application is not impersonating. If the application is impersonating via , the identity will be the anonymous user (typically IUSR_MACHINENAME) or the authenticated request user.

To grant ASP.NET access to a file, right-click the file in Explorer, choose "Properties" and select the Security tab. Click "Add" to add the appropriate user or group. Highlight the ASP.NET account, and check the boxes for the desired access.


I'm using forms authentication, is this a factor? What I also don't get is that my file upload control can save files to this directory.

Thanks for any help you can give Confused


Ed
GeneralCannot open file
Singh Saab
7:12 24 Aug '07  
Hi there,

The user is prompted to either Open or Save or Cancel. If the user clicks Open then the appropriate application will open (i.e. MS Word) but then an error message is displayed saying that it cannot find the file requested. The path given points to the temp directory on the clients machine.

Suggestions?
GeneralASP version
wickyguru
15:59 23 May '07  
Is there a ASP version of the Secure File Download page application??
I'm usinge ASP and not asp.net.
Hope anyone can help me.
Thank you very much!

Sincerely,
Wicky
GeneralRe: ASP version
paulclift
5:38 24 Mar '09  
wickyguru wrote:
Is there a ASP version of the Secure File Download page application??



Long time after you've asked this, but I needed to use something like this in an ASP app we have running so I wrote this using the aspx version as a guideline.

	if CanDownload() then
strFileName = Request.QueryString("filename"))
strUPLOADdir = "c:\home\default\mydir\"

Response.AddHeader "
Content-disposition", "attachment; filename=" & strFileName
Response.ContentType = "
application/octet-stream"
adTypeBinary = 1

Set BinaryStream = CreateObject("
ADODB.Stream")

BinaryStream.Type = adTypeBinary

BinaryStream.Open

BinaryStream.LoadFromFile strUPLOADdir & strFileName

Response.BinaryWrite BinaryStream.Read

Response.End
else
Response.Write "
Sorry you do not have access to this file, please return to the homepage and login." end if
%>

QuestionMultiple downloads problem
Petrus Theron
13:17 17 Jan '07  
Hi Sean

Using code similar to this, we seem to be having a problem when a user attempts to download two concurrent links that both use this script. As soon as the first download completes, the second commences. This occurs regardless of the browser used.

It seems as if IIS blocks until the first download completes, upon which the second download immediately starts. I have queried our US-based host about this problem, but they are adamant that it is script-related.

Thanks for a great article!

Kind regards
Petrus Theron
GeneralGreat but....
Filippo Macchi
4:46 17 Jan '07  
it's possible to open a new browser window when click on button to download file?
Generalasp.net account access denied
tdalsimer
9:13 16 Jan '07  
Everything works fine on my local machine; when I deploy on the production server - I get an "access denied message".

I have set the security to the download folder to "full control" for the asp.net machine account.

The only way I can get it to work on the production server is to give full control to the
download folder to the "allusers" account which I suspect is a no no.

Any thoughts?

Thanks

Tom
GeneralRe: asp.net account access denied
tdalsimer
12:01 16 Jan '07  
Problem solved - here is what I did:

I gave the Network Service Account modify priv on the download folder. You may ask why? I found that the default Application pool "defAppPool" is running with the Network Service Account identity.

I don't know if this is a security risk, but it is working.
Questionquick question
rahul_l10
17:04 15 Jan '07  
I have done this same thing slightly in a different way but it doesnt matter...my question is if someone knows the exact location of the pdf file and tries to open directly by typing in the whole url in the browser the pdf file opens up without any authentication. How to stop the anonymous user from viewing the pdf file directly? I hope you have the answer for this situation coz i m stuck with this problem for quiet a while now...


AnswerQuick answer
jermanis
7:26 15 Feb '07  
Rahul,

This is covered in the article. To prevent this case, you don't store the file within the web server. You keep the file in a folder like c:\Files; this folder is not accessible from the web server.
GeneralPerformance of this method
aronitin
23:41 3 Nov '06  
Hi Sean,

My question is it will create a memory bottleneck for the asp.net process, if concurrent users will try to download the file.

Do you have any idea how to improve the server performance during concurrent download in a load balanced web farm environment?

Nitin Arora

QuestionSSL, anyone?
jay_dubal
2:36 13 Oct '06  
Hello,

I am facing problem in IE while doing Response.WriteFile over HTTPS://, it gives encrypted characters in file name in download dialog, the file size is fine AND FINALLY the download never happens...

Anyone faced this problem?

is response.writefile() behaving differently for SSL?

Thanks
AnswerRe: SSL, anyone?
jay_dubal
4:09 26 Jun '07  
yes the response.writefile() behaves differently for SSL, and the problem that you have mentioned will occur... to solve this problem make sure you are not clearing browser cache / OR preventing the caching from the code....

Let me know if that solves the problem..

Jay Dubal

( need ebooks? http://www.free-ebooks-download.org }

GeneralRe: SSL, anyone?
mahabir
3:09 27 Oct '08  
You were a life saver.
Thanks a lot man. Yes, that worked.
Cheers!!!
GeneralWhy does my brower close upon download only with .zip file type?
Jeff Cecchini
12:43 24 Sep '06  
I have implemented this code and have discovered something very annoying. Can anyone help out with this one?

Why does my brower close upon download only with .zip file type?

It doesn't on pdf's and .txt files.

Thanks in advance, Jeff

Here's my code:

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
Try
Dim fs As FileStream
Dim strContentType As String
Dim strPath As String = _
System.Configuration.ConfigurationSettings.AppSettings("FileDownloadDir") & "\"
Dim FileExtention As String

' Get File name from Url parameter
Dim strFileName As String = Request.Params.Item("File")

' Make sure user did not try to inject a path. We just want a file name!
strFileName.Replace(":", "")
strFileName.Replace("\", "")
strFileName.Replace("*", "")

' If the file exists, proceed with download
If Not (strFileName Is Nothing) Then
If File.Exists(strPath & strFileName) Then
fs = File.Open(strPath & strFileName, FileMode.Open)
Dim bytBytes(fs.Length) As Byte
fs.Read(bytBytes, 0, fs.Length)
fs.Close()

' Select Content type header info by file extention.
FileExtention = IO.Path.GetExtension(strPath & strFileName).ToLower
Select Case FileExtention
Case ".txt"
strContentType = "text/plain"
Case ".pdf"
strContentType = "application/pdf"
Case ".zip"
strContentType = "application/zip"
Case Else
strContentType = "application/octet-stream"
End Select

Response.Clear()
Response.AddHeader("Content-disposition", "attachment; filename=" _
& strFileName)
Response.ContentType = strContentType
Response.BinaryWrite(bytBytes)
Else
Response.Write("File does NOT exist!")
End If
Else
Response.Write("No file was selected!")
End If
Catch ex As System.Exception
basGlobal.ErrorProcessingForSystemException(ex, "Problem with file download", _
"Page_Load", Me, True)
End Try

Response.End()
'Response.Flush() ' did not improve problem, same behavior

Response.Redirect("DownloadSelection.aspx")

End Sub



Jeff Cecchini

GeneralHello Fellow Saffa Programmer
cykophysh39
22:50 20 Sep '06  
I came across your article while researching one of my own.
II read your bio, and you're from Joey's, SO I thought I would say howzit, as I am origianlly from Edenvale, JHB, but now living in UK. I don't often see Saffa Programmers around. I think the only other one I have seen, has been here on Code Project too. Big Grin

"a fool will not learn from a wise man, but a wise man will learn from a fool"

"It is hard to fail, but it is worse never to have tried to succeed." - Theodore Roosevelt

"Success is going from failure to failure without losing your enthusiasm." - Winston Churchill

Generalno window.document
koolbones
14:08 30 Aug '06  
Hi

I implemented this code in my project and it works just fine. but I've got an issue. in the caller page, I've got an action button which exec some javascript, it works fine, but after I clicked the download button, and downloaded the file, it seems there's not window.document object anymore, so when the javascript hits the line "document.getElementById", an error is thrown.

anyone can help me out of this?

--

GeneralClose the open browser window.
Paddy Boyd
4:09 13 Jul '06  
Hey,

I have similar code to yours running on a system, the streaming window is opened and it downloads the file with the download dialog, all well and good. We have a request for the opened (blank) IE window that is left be then closed (once the user has chosen to save or open the download). Do you have any idea how this may be done?
GeneralThread aborted exception
*Butterfly*
23:49 6 May '06  
The Response.End() at the end of the code gives me "Thread was being aborted" exception.
any clue?
Thanks

-- modified at 4:52 Sunday 7th May, 2006


Last Updated 20 Apr 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010