SitePoint Sponsor

User Tag List

Results 1 to 22 of 22
  1. #1
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Securing PDF Files & Some Server Actions

    Hey, I'm gonna be starting a big project that will include using PDF files. The only thing is, I don't want someone to be able to just type the filename and get to it.


    Now, I need to either:
    1) display the PDF in an ASP page, below some other information or
    2) link to the PDF page. The only thing is I don't want to link to the file where people can know the file location and name.

    What should I do?

    Is it possible to make a folder on the server that if someone types the filename, they won't be able to get to the file? If so, how would I give them access to the file when they did need it? Is there some type of "include" statement for PDF files so that if the user was logged in it would include the file, but if not then they couldn't get to it?

    Another option I've heard of is renaming all the file extensions to something the server won't allow you to open. Then when it is time for someone to open a PDF if they are logged in, the server creates a copy of the PDF in a different location with a random filename and a PDF extension. Then after so many minutes or even if it had to be done manually every so often... the temporary PDF file would be deleted.

    So using one of those options, could someone tell me how to do that?

    Thanks so much...

  2. #2
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PS - If any of this is on the web somewhere (I'm sure it is), please direct me to some good resources as well. I just haven't found anyhting on it.

  3. #3
    SitePoint Wizard Goof's Avatar
    Join Date
    Feb 2001
    Location
    Pittsburgh, PA
    Posts
    1,154
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    fillup,

    I wanted to do the same thing. Check this thread for how I will accomplish it.

    Hope that helps,
    Goof
    Nathan Rutman
    A slightly offbeat creative.

  4. #4
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks goof. That thread (especially M@rco's subs) will be helpful I think. Only thing I don't quite understand...

    The file can simply be in a folder that allow access, correct? But then it will open the Acrobat and allow access?

    Thanks

  5. #5
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    And in this line:
    Code:
    sub SendStreamToBrowser(FileStream, FileName, ContentType, IsInline)
    What is "ContentType" do... and when would you use True, and when False?
    Last edited by fillup07; Jul 18, 2004 at 11:14.

  6. #6
    SitePoint Wizard Goof's Avatar
    Join Date
    Feb 2001
    Location
    Pittsburgh, PA
    Posts
    1,154
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    No no...the content type is not a true/false variable, but should be a string containing the content type for the document. For PDFs, the content type is "application/pdf". M@rco just put in an extra step that defaults the content type to "application/octet-stream" if you pass False instead of a real content type.

    As for how this script would work, it's not about giving Acrobat access to a file, but delivering the file the browser only if the user has permission. Let me explain:

    You would put the PDF files in a directory that your IUSR_machine system account has access, but that is invisible to your web server (i.e. c:\inetpub\bin). If you have a shared hosting account, just e-mail your system administrator and ask them to create a subfolder called "bin" (or whatever you want it called) and to disable anonymous web access to this directory through IIS (it should take them about 30 seconds when they actually sit down to do it). This script then acts like a gateway between the IUSR_machine account (which can see and access the PDFs), and the request coming in through the web server (which can't see or access the PDFs). The script (and this is the part you would have to write) would first check to see if the user is authorized to access the file (through a cookie, session variable, or whatever), and if they do have access would use M@rco's code to simultaniously read from the PDF in the restricted folder and write it to the browser. Because of the content type setting, the browser thinks it's getting a PDF and not an HTML page, so it loads up Acrobat and displays the file.

    The system has little to do with anything on the client-side, but is all about the server-side authorizing and then delivering the PDF. So basically, the script that you would create would be something like getPDF.asp which determines authorization and then sends either the PDF or some HTML error for people who don't have access.

    Does that clear the procedure up a little bit?
    Goof
    Nathan Rutman
    A slightly offbeat creative.

  7. #7
    The doctor is in... silver trophy MarcusJT's Avatar
    Join Date
    Jan 2002
    Location
    London
    Posts
    3,509
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Succinctly put Goof! It's also quite odd to see someone else explaining my code!!!
    MarcusJT
    - former ASP web developer / former SPF "ASP Guru"
    - *very* old blog with some useful ASP code

    - Please think, Google, and search these forums before posting!

  8. #8
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Okay, yea I get it now... I saw that in the example that it had "False" for the content type, but now I notice that it's not a True/False.... it's a False/Some other string.

    I see that "False" is "application/octet-stream". What exactly is that, and when would I use it?

    And, if I am only going to be using this for PDF's, should I just modify the code to make ContentType always equal "application/pdf"?

    Thanks!

  9. #9
    The doctor is in... silver trophy MarcusJT's Avatar
    Join Date
    Jan 2002
    Location
    London
    Posts
    3,509
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    In the full code in my library, there's actually a third option (empty string) which calls another function which derives the correct MIMEType from the file's extension, but to make the code shorter, I removed that chunk so that the code would be complete without having to include that other function as well.

    I see that "False" is "application/octet-stream". What exactly is that, and when would I use it?
    That basically indicates to the browser that it is a binary file stream (an octect = 8 bits = 1 byte). It's essentially a generic MIMEType that will work for any file that you send (text, image, one you invented) if you don't know or can't be arsed (!) to supply the correct MIMEType. Since browsers decide what to do with the file based on the ContentType (which is the same thing as the MIMEType, BTW) if you send "application/octet-stream" then the browser SHOULD always prompt to save the file instead of attempt to display it (with or without a plugin).

    However, since IE thinks it's so clever, it sometimes (depending on browser version, wind speed, and the colour of Bill Gates' morning stool) tries to have a go anyway, by looking at the file extension. So just to make sure, there's also that bit about the "Content-Disposition" header, which IE does obey! (As it happens, I don't know if any other browsers pay attention to this header)

    So, to answer your question, if you want the client/browser to try to display the file inside the browser window (using internal support or plugins) then you should supply the correct ContentType/MIMEType for that file type, and also set the IsInline parameter to True.

    Otherwise, to force the client/browser to save the file, set the ContentType parameter to False (you can manually set it to "application/octet-stream" if you really want to, but the False just makes life easier), and set the IsInline Parameter to False.

    And, if I am only going to be using this for PDF's, should I just modify the code to make ContentType always equal "application/pdf"?
    No! Just hardcode the "application/pdf" bit in the SendStreamToBrowser() call if you have to, but don't modify the function! That defeats the point of having parameters!

    Just stick it in an include somewhere and leave the poor thing alone!

    Finally, here is the full, current, and unedited code for the SendStreamToBrowser (and helper LoadStream and GetMIMEType) functions as they appear in my library:
    Code:
    'Load a file from disk
    Function LoadStream(FilePath)
    	Dim objStream
    
    	Set objStream = Server.CreateObject("ADODB.Stream")
    
    	objStream.Type = 1 'adTypeBinary=1
    	objStream.Open
    
    	objStream.LoadFromFile FilePath
    	LoadStream = objStream.Read
    	
    	objStream.Close
    	Set objStream = Nothing
    End Function
    
    
    'returns the MIME header type for a given extension
    Function GetMIMEType(Extension)
    	dim Ext
    	Ext = UCase(Extension)
    	
    	select case Ext
    
    		'Common documents
    		case "TXT", "TEXT", "JS", "VBS", "ASP", "CGI", "PL", "NFO", "ME", "DTD"
    			sMIME = "text/plain"
    		case "HTM", "HTML", "HTA", "HTX", "MHT"
    			sMIME = "text/html"
    		case "CSV"
    			sMIME = "text/comma-separated-values"		case "JS"
    			sMIME = "text/javascript"
    		case "CSS"
    			sMIME = "text/css"
    		case "PDF"
    			sMIME = "application/pdf"
    		case "RTF"
    			sMIME = "application/rtf"
    		case "XML", "XSL", "XSLT"
    			sMIME = "text/xml"
    		case "WPD"
    			sMIME = "application/wordperfect"
    		case "WRI"
    			sMIME = "application/mswrite"
    		case "XLS", "XLS3", "XLS4", "XLS5", "XLW"
    			sMIME = "application/msexcel"
    		case "DOC"
    			sMIME = "application/msword"
    		case "PPT","PPS"
    			sMIME = "application/mspowerpoint"
    		
    		'WAP/WML files	
    		case "WML"
    			sMIME = "text/vnd.wap.wml"
    		case "WMLS"
    			sMIME = "text/vnd.wap.wmlscript"
    		case "WBMP"
    			sMIME = "image/vnd.wap.wbmp"
    		case "WMLC"
    			sMIME = "application/vnd.wap.wmlc"
    		case "WMLSC"
    			sMIME = "application/vnd.wap.wmlscriptc"
    			
    		'Images
    		case "GIF"
    			sMIME = "image/gif"
    		case "JPG", "JPE", "JPEG"
    			sMIME = "image/jpeg"
    		case "PNG"
    			sMIME = "image/png"
    		case "BMP"
    			sMIME = "image/bmp"
    		case "TIF","TIFF"
    			sMIME = "image/tiff"
    		case "AI","EPS","PS"
    			sMIME = "application/postscript"
    			
    		'Sound files
    		case "AU","SND"
    			sMIME = "audio/basic"
    		case "WAV"
    			sMIME = "audio/wav"
    		case "RA","RM","RAM"
    			sMIME = "audio/x-pn-realaudio"
    		case "MID","MIDI"
    			sMIME = "audio/x-midi"
    		case "MP3"
    			sMIME = "audio/mp3"
    		case "M3U"
    			sMIME = "audio/m3u"
    			
    		'Video/Multimedia files
    		case "ASF"
    			sMIME = "video/x-ms-asf"
    		case "AVI"
    			sMIME = "video/avi"
    		case "MPG","MPEG"
    			sMIME = "video/mpeg"
    		case "QT","MOV","QTVR"
    			sMIME = "video/quicktime"
    		case "SWA"
    			sMIME = "application/x-director"
    		case "SWF"
    			sMIME = "application/x-shockwave-flash"
    
    		'Compressed/archives
    		case "ZIP"
    			sMIME = "application/x-zip-compressed"
    		case "GZ"
    			sMIME = "application/x-gzip"
    		case "RAR"
    			sMIME = "application/x-rar-compressed"
    			
    		'Miscellaneous
    		case "COM","EXE","DLL","OCX"
    			sMIME = "application/octet-stream"
    			
    		'Unknown (send as binary stream)
    		case else
    			sMIME = "application/octet-stream"
    	end select
    	
    	GetMimeType = sMIME
    End Function
    
    
    'Sends the specified file to the browser
    sub SendStreamToBrowser(FileStream, FileName, ContentType, IsInline)
    	Dim FileExt, FileSize
    	
    	'Disable error checking
    	on error resume next
    
    	'Clear buffer
    	Response.Clear
    	
    	FileExt = mid(FileExt, instrrev(FileName,".") + 1)
    	FileSize = Ubound(FileStream) + 1
    		
    	'Add filename to header
    	Response.AddHeader "Connection", "keep-alive"
    	Response.AddHeader "Content-Length", FileSize
    	
    	'Check if data should be delivered inline or not
    	If IsInline = True then
    		'Allow the browser to render the file inside a browser window (if it can)
    		Response.AddHeader "Content-Disposition","inline; filename=" & FileName
    	Else
    		'Force browser to save file
    		Response.AddHeader "Content-Disposition","attachment; filename=""" & FileName & """"
    	End If
    	
    	'Get ContentType for download
    	select case ContentType
    		case false
    			'Generic binary ContentType and Charset
    			Response.ContentType = "application/octet-stream"
    			Response.Charset = "UTF-8"
    			
    		case ""
    			'Find out what it should be
    			Response.ContentType = GetMIMEType(FileExt)
    		
    		case else
    			'Use the ContentType that was passed
    			Response.ContentType = ContentType
    	end select
    
    	'Send data to client
    	Response.BinaryWrite(FileStream)
    	Response.Flush
    end sub
    Since this is the thread in which it has been most comprehensively discussed, please post any bugs, suggestions, problems, etc here!

    The time is now 03:04am, so I'm going to bed!
    ZZZZZZZZZzzzzzzzzzzzzz.........!


    Edit:

    Added source to LoadStream so that you don't have to trackit down from another one of my posts

    Edit:

    Changed VBS vB tags to CODE tags to preserve formatting

    Edit:

    Added CSV to GetMIMEType()
    Last edited by M@rco; Oct 29, 2003 at 05:37.
    MarcusJT
    - former ASP web developer / former SPF "ASP Guru"
    - *very* old blog with some useful ASP code

    - Please think, Google, and search these forums before posting!

  10. #10
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You the man, M@rco. Thanks so much. I'll try it soon, and I suspect no problems.

    Oh, and BTW, I think I am going to edit your function to always use the PDF contenttype... muhahahaha

  11. #11
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh, what would happen if I sent an ASP page using your functions? Would it parse the ASP (and execute the code) or would it just display as text?

    If it would execute it (as I suspect), would it be possible to display the code of an ASP file?

  12. #12
    The doctor is in... silver trophy MarcusJT's Avatar
    Join Date
    Jan 2002
    Location
    London
    Posts
    3,509
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The browser certainly won't parse the code (only the server can do that), but if you sent it as "text/plain" - which it will do if you leave it to autodetect using GetMIMEType() - and set the IsInline parameter to True then it should display as text in the browser.

    That's the theory, but does it work in practice?
    Try it and let me know!
    Last edited by M@rco; Jul 10, 2002 at 04:14.
    MarcusJT
    - former ASP web developer / former SPF "ASP Guru"
    - *very* old blog with some useful ASP code

    - Please think, Google, and search these forums before posting!

  13. #13
    The doctor is in... silver trophy MarcusJT's Avatar
    Join Date
    Jan 2002
    Location
    London
    Posts
    3,509
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    IMPORTANT

    Please all note that I have edited the code above to correct the dumb error that I made which Goof encountered here. Apologies!

    (I really should try to get into the habit of sleeping more than 4 hours a night - still trying to adjust to civillian life after Uni! )

    Edit:

    dud link corrected
    Last edited by M@rco; Jul 13, 2002 at 04:26.
    MarcusJT
    - former ASP web developer / former SPF "ASP Guru"
    - *very* old blog with some useful ASP code

    - Please think, Google, and search these forums before posting!

  14. #14
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Why do I get a "You do not have permission to access this page." when I click that link?

    Anyways, thanks for the update.

  15. #15
    The doctor is in... silver trophy MarcusJT's Avatar
    Join Date
    Jan 2002
    Location
    London
    Posts
    3,509
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Fillup07, thanks for pointing that out - I had accidentally linked to the "Edit Post" of my last post in that thread rather than simply to that thread!!! DOH!
    (It was the end of a VERY long day.... today's looking MUCH better already)
    MarcusJT
    - former ASP web developer / former SPF "ASP Guru"
    - *very* old blog with some useful ASP code

    - Please think, Google, and search these forums before posting!

  16. #16
    The doctor is in... silver trophy MarcusJT's Avatar
    Join Date
    Jan 2002
    Location
    London
    Posts
    3,509
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Adding some keywords so that this thread is found more easily in future, since questions about forcing a download are asked once a week or so...

    save filename http force file download send mime content type header asp php jsp extension stream binary
    Last edited by M@rco; Mar 26, 2004 at 21:19.
    MarcusJT
    - former ASP web developer / former SPF "ASP Guru"
    - *very* old blog with some useful ASP code

    - Please think, Google, and search these forums before posting!

  17. #17
    The doctor is in... silver trophy MarcusJT's Avatar
    Join Date
    Jan 2002
    Location
    London
    Posts
    3,509
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    And here's an all-in-one sub to send the specified file to the browser...
    Code:
    'Wrapper for quick 'n' easy file sending to browser
    Sub SendFileToBrowser(FilePath, IsInline)
    	Dim Stream, FileName
    
    	Stream = LoadStream(FilePath)
    	FileName = mid(FilePath, instrrev(FilePath,"\") + 1)
    
    	SendStreamToBrowser Stream, FileName, "", IsInline
    End Sub
    MarcusJT
    - former ASP web developer / former SPF "ASP Guru"
    - *very* old blog with some useful ASP code

    - Please think, Google, and search these forums before posting!

  18. #18
    Also available in Large Si's Avatar
    Join Date
    Sep 2002
    Location
    Walsall, UK
    Posts
    1,911
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    M@rco, you're a life saver!! That is a superb piece of script... cheers!!!!
    Si
    Are you a Photoshop Jedi Master? Prove it!

    Is funky house your bag? You'll love this!

    Voice
    , eyes, ears, body and hands.


  19. #19
    The doctor is in... silver trophy MarcusJT's Avatar
    Join Date
    Jan 2002
    Location
    London
    Posts
    3,509
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    MarcusJT
    - former ASP web developer / former SPF "ASP Guru"
    - *very* old blog with some useful ASP code

    - Please think, Google, and search these forums before posting!

  20. #20
    Phil fillup07's Avatar
    Join Date
    May 2002
    Location
    Jacksonville, FL
    Posts
    1,168
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes... I've been using this for quite a while now and it's very helpful.

  21. #21
    Procrastinator Extraordinaire lucas's Avatar
    Join Date
    Oct 2000
    Location
    Springfield, Missouri, U.S.A.
    Posts
    320
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    M@rco, your awesome.

  22. #22
    SitePoint Member
    Join Date
    May 2005
    Posts
    1
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I seem to be having problems with large files(larger than about 4MEGS). If the file is under that file size the file will download fine. But otherwise it's downloaded extremely quickly and the filesize downloaded is "0".


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •