Help - Search - Members - Calendar
Full Version: Protected files website
W3Schools Forum > Server Scripting > ColdFusion
Pages: 1, 2
vchris
I'm developing this site which will have a couple different logins. These logins will belong to a certain group or province I should say. So for example, the province of Ontario (canada) will login using their login and should only be able to see their documents (webpage, excel, word, powerpoint...) same goes for other provinces and groups. A group or province should not be able to access another ones files. The problem I have with this is if someone knows the url of the excel spreadsheet, what's stopping that person to type it in and open a file that he's not supposed to have access to? So is there a way I can protect these files from being opened just by putting the url in? I am kinda limited with web server settings, is there something I can do with ColdFusion?
mma_fighter123
Stick the files outside of the web root directory. I don't know much Coldfusion, but since you know PHP I will try to explain it that way. Just include() the file with the appropriate content-type header.
vchris
QUOTE (mma_fighter123 @ Dec 10 2007, 03:51 AM) *
Stick the files outside of the web root directory. I don't know much Coldfusion, but since you know PHP I will try to explain it that way. Just include() the file with the appropriate content-type header.

Ok but by including it, will it look the same as if the user clicked a download link? There is no way to get the path of the included page?
mma_fighter123
When you include the file, you have to supply the appropriate header for it, otherwise it will show the code of the file which isn't pretty. If you include the file, it will either allow them to download as if they clicked a link or embed it in the browser like you would see for example pdf files. They can't type a URL to get to the file that is outside the web root (this would be a huge security risk if all the files on your server was available on the web).
vchris
I have the word doc displaying except it's not really readable. There's a bunch of weird characters. I used cfinclude to display the file but I don't have any header type to supply... The thing is I want the users to be able to download it just like a direct link to the doc not just displaying the doc.

I'm looking for a solution to displaying the doc correctly...
mma_fighter123
From php.net with the header function.

If you want the user to be prompted to save the data you are sending, such as a generated PDF file, you can use the » Content-Disposition header to supply a recommended filename and force the browser to display the save dialog.

CODE
<?php
// We'll be outputting a PDF
header('Content-type: application/pdf');

// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');

// The PDF source is in original.pdf
readfile('original.pdf');
?>
vchris
I have no idea how to do this in CF...
mma_fighter123
Do you know how to work with headers in Coldfusion? If you know how to send your own, just copy and paste what is between header( and ) and replacing the names of the file and application/pdf to whatever the mime type is for the file you chose to use, and just include the file.
Skemcin
vchris - sorry I've been really busy at work but I can help you with this. First, mma_fighter123's approach is on point. In ColdFusion, the best way to do this is to use <cffile> to "read" the file. Set the contents of the file to a variable using <cfsavecontent>. Then you do as mma_fighter123 suggest and use

CODE
<cfheader name="Content-Type" value="application/msword"/>
<cfheader name="Content-Disposition" value="inline; filename=""#your_desired_filename#"""/>
<cfheader name="Content-Description" value="#your_cffile_content#"/>
<cfoutput>#content#</cfoutput>


* if any client is using Windows Vista you have another work around since Word has a new mime type in that OS. Can't think of it off the top of my head but should be able to Google it.
vchris
ok so cfheader will load the doc, but will it load the application in the browser or will it ask to download? So the content-type needs to be set accordingly each time right? I can open pdf, word, excel, zips...? I want users to be asked to download each time if possible. I don't want to content of the file to be read and then displayed on the webpage without the app.
Skemcin
Well, the application that loads the file is dependent upon how each individual user's browser MIME file types are configured. You, as a developer, have limited control over how the client actually handles the request they make. If you dynamically deliver the file (pdf, doc, xls, etc.) then all you can do is tell the browser what it is (other than HTML, XML, etc.). If the browser has "msword" defined to use Photoshop (for some reason) you can't do anything about that. All you can do is deliver the content with the correct MIME type and then wash your hands.

After looking it over and finally getting a second to work an example - its even easier that what I posted before:
CODE
<cfset myFile="[physical path to your file]" />
<cfheader name="Content-Disposition" value="inline; filename=myfile.doc" />
<cfcontent type="application/msword" file="#myFile#" />


having said that, I have (somewhere I will find it) a function that you would run #myFile# in that will return the mime type to use in your <cfcontent> tag.

I'll post it when I find it...
vchris
Thanks I'll try that probably tomorrow. I'll let you know.

Wouldn't the function be just a bunch of cases?

case "doc":
case "xls":
...
aspnetguy
or a series of if else if you prefer wink.gif
Skemcin
Yes. In this situation you could use:
  • <cfif><cfelseif><cfelse></cfif>
  • <cfswitch><cfcase></cfcase><cfdefaultcase></cfdefaultcase></cfswitch>
  • or all that in <cfscript>
The example I have to (still) track down uses <cfscript>.

Generally, if you are evaluating a variable, <cfswitch> is best since it is assumed (in your logic) that ColdFusion already knows the variable. So ColdFusion will only process up until the <cfase> is found - a slight performance benefit.

If you are going to use the isDEFINED("") function, then you have to use <cfif> or <cfscript>. If the situation you are dealing with has only a couple conditions, then <cfif> is fine. If your situation is rather involved or lengthy, then you will have a marginal performance benefit using <cfscript> since ColdFusion doesn't need to process the actual tags.

In any case, you are correct, the function is merely a slue of conditions that accommodate the various file types. The reason why you might want to use it (ince I get a second to find it) is that there are tons of MIME extensions that would be helpful to throw in there to avoid errors or mis-associated application types.
vchris
I rarely, if ever, used <cfscript>. Is it handled by javascript?
vchris
I've been playing with your script skemcin and I download the file, it's great, except that the file I download is blank. If I create a test.doc file with "This is a test document" in it. After downloading I got nothing same with other 4mb docs.
Skemcin
QUOTE (vchris @ Dec 13 2007, 09:12 PM) *
I rarely, if ever, used <cfscript>. Is it handled by javascript?

No. Javascript is client side, <cfscript> is stilll server side. It is basically the raw format of the commands that ColdFusion executes. ColdFusion just developed its own internal functions that essentially parse the tags from typical ColdFusion code to get to the nit-n-gritty. Some argue that it is easier to read in certain circumstances. Essentially, the two blocks of code below are the same - the <cfscript> code runs a fraction faster since ColdFusion doesn't have to parse the actual <cf> jargon:

CODE
<cfset thcolor="##E4F1F8">
<cfset evenrow="##F7F7F7">
<cfset darkline="##309ACF">
<cfset lightline="##BEE3F3">
<cfset fontfaces="Verdana,Arial">
<cfset repRowTxtColor="##666666">
<cfset repTtlTxtColor="##3398CC">
<cfset reportTitle="text-align:left;font:#fontfaces#;font-weight:bold;font-size:14px;color:##3398CC;">
<cfset reportSelectbox="text-align:left;font:#fontfaces#;font-weight:bold;font-size:12px;color:##3398CC;">
<cfset reportDate="font:#fontfaces#;font-weight:bold;font-size:10px;color:##3398CC;">
<cfset reportHeaderRow="font:#fontfaces#;font-size:11px;color:##3398CC;vertical-align:bottom;">
<cfset reportRow="font:#fontfaces#;font-size:10px;color:##666666;vertical-align:middle;">
<cfset reportSubText="text-align:left;font-weight:italics;font-size:10px;color:##666666;">
<cfset reportSubTextNI="text-align:left;font:#fontfaces#;font-size:10px;color:##3398CC;">
<cfset printoutRow="font:#fontfaces#;font-size:10px;color:##666666;line-height:16px;vertical-align:bottom;">

is the same as
CODE
<cfscript>
thcolor="##E4F1F8";
evenrow="##F7F7F7";
darkline="##309ACF";
lightline="##BEE3F3";
fontfaces="Verdana,Arial";
repRowTxtColor="##666666";
repTtlTxtColor="##3398CC";
reportTitle="text-align:left;font:#fontfaces#;font-weight:bold;font-size:14px;color:##3398CC;";
reportSelectbox="text-align:left;font:#fontfaces#;font-weight:bold;font-size:12px;color:##3398CC;";
reportDate="font:#fontfaces#;font-weight:bold;font-size:10px;color:##3398CC;";
reportHeaderRow="font:#fontfaces#;font-size:11px;color:##3398CC;vertical-align:bottom;";
reportRow="font:#fontfaces#;font-size:10px;color:##666666;vertical-align:middle;";
reportSubText="text-align:left;font-weight:italics;font-size:10px;color:##666666;";
reportSubTextNI="text-align:left;font:#fontfaces#;font-size:10px;color:##3398CC;";
printoutRow="font:#fontfaces#;font-size:10px;color:##666666;line-height:16px;vertical-align:bottom;";
</cfscript>


QUOTE (vchris @ Dec 14 2007, 09:49 AM) *
laugh.gifI've been playing with your script skemcin and I download the file, it's great, except that the file I download is blank. If I create a test.doc file with "This is a test document" in it. After downloading I got nothing same with other 4mb docs.

hmmm - I tested this on my localhost where the physical path was c:\temp\my924brochure.pdf with my web application in c:\inetpub\wwwroot\getdoc\ and I didn't have any issues. I am running IIS6 and ColdFusion 8 Developer License on this PC.

I'll look into this after lunch (got a couple "critical issues" delivered to me).
vchris
I guess this has to do with the path to the docs being incorrect or something? I used relative paths (../../test.doc) since I don't know the absolute path from the drive.
Skemcin
and that worked?

ColdFusion (at least before CF8) always needed the full absolute path whenever the physical path was required. I think CF8 can do either one. The relative path is always the web site relative path, never the physical server path (for security reasons).
vchris
using ../../test.doc didn't work. It is the correct path. I can probably find out in which drive I am and use absolute path.
Skemcin
To find the current location of the file, you can do this:
#cgi.CF_TEMPLATE_PATH#
or
#cgi.PATH_TRANSLATED#

to which you can:
#replace(cgi.CF_TEMPLATE_PATH,"#cgi.SCRIPT_NAME#","[your file name]","all")#

You will need to use the absolute physical path.
vchris
It works. Did you find that script about the mime types?
Skemcin
I looked the other day and again today, and I can't find what I was hoping for - nothing that will save you any length of time anyway.

I'll look at home again, but I'm sorry if I can't locate it.
vchris
No worries I built my own. It's working great.

Is there any way I can know if a file is not found? I got it working with the querystring right now (?myFile=test.doc). If someone were to manually enter ?myFile=testd.doc it'll still try to download but could I have a message say file not found instead of downloading a blank doc?
Skemcin
Use the FileExists() function with a <cfif> statement.

CODE
<cftry>
<cfif fileexists(#yourfilesphysicalpath#)>
what you want to do if it is there
<cfelse>
what you want to do if the file is not found
</cfif>
<cfcatch type="any">
What you do if any type of error occurs in the process.
</cfcatch>
</cftry>
vchris
Works awesome! Thanks skemcin biggrin.gif
Skemcin
Your welcome - glad to see another CF success.
vchris
I got another idea to improve the security of the files for this website. Since a user could download any file if he knows its name. On logon, I could set a session variable of the group the user is from. Then have a folder for each group within the docs folder. That user can then only download a file from his folder.
mma_fighter123
What if they typed something like ../folder_they_shouldn't_be_seeing/whateverfile.doc? In PHP you can use basename() to take out ../folder names/ from the input so it would be whateverfile.doc and then where you check file exists, add the folder name they are allowed to look at.

CODE
<?php
  $file=basename($_GET['file']); // ../some_other_folder/file.doc to file.doc
  if (file_exists($_SESSION['group'].'/'.$file)) {
    // use header and include
  }
?>


I also found out when playing around with my own code is if you use special characters like *, <, etc. it can grab files based on the filesystem you are using (Windows for me). You could probably make an array of files to choose from the code or have Coldfusion scan the folder for all of the doc files and check if it's in that array of files.
vchris
They can't type a direct url to the file since the file is stored on the server outside of the web folder. Only ColdFusion can get in there and get the file. I made a coldfusion page that will fetch the file. The user has no idea where the file is. There is only a querystring (?myFile=test.doc). The current problem with this is all the files are all in one folder so if a user from one group knows the name of a file from another group, he could access it with the querystring. I'll be adding the group folders so this won't be possible. I know how to do this part. Thanks anyway for the code.
mma_fighter123
You might want to make the folder names kind of random then. They can just go up one level with ../ name of folder and the file name.

To get an idea of what someone can do.
http://www.owasp.org/index.php/Testing_for...ctory_Traversal
http://www.owasp.org/google/results.html?c...=FORID%3A9#1150
vchris
Could a user go up one level if my path is hard coded except for the file name?

This is the kinda path I have: /absolutepathtofolder/group/file.doc. This path is outside of the web folder. You mean that someone could change my querystring to ?myFile=../othergroup/file.doc and access another file? Is this valid then /absolutepathtofolder/group/../othergroup/file.doc since this is how it will be displayed. I could also simply not allow / in the querystring.
mma_fighter123
I don't think that would be good enough, if Coldfusion has a way of just returning the filename like PHP's basename() or use regular expressions that only accepts certain characters like A-Za-z0-9_.
Skemcin
This should be easily resolved by creating two more tables, one to define groups and another as a lookup.

If you user table is "user" and you group table is "group" and your lookup table is "user_x_group" then name your folders the same way you name your groups. When the user logs in and is authenticated, then all your file requests would run off a variable you could set based on their ID in the "user_x_group" table as it pertains the the assigned group.

I would even take this one step farther - if its possible and have the documents they are requesting databased too. Your "document" table could then store the filename and then you could either move them in seperate folders or keep them all in one and then create a "group_x_document" table that assigns documents to single or multiple groups without having to have copies of file in different group folders. The link to the document would now have a UUID as the string and not the actual filename.

Then, on the ColdFusion page that actually requests the file, you have to query the "document" table to get the real file, then you get the file's ID and look it up in the group table to see if this user is allowed to even have it.

In the latter example, the user never has any idea of what group they are in (which doesn't matter if they do) but additionally they NEVER see the actual filename of the file since the URL string is a UUID and not the file name. If you are using the code we talked about earlier, then you can rename the file as it is prompted to download so that it's not even remotely the same as it's physical defined on the server.

Make sense? Let me know if you need me to back up and re-imagine that [admittedly typed a little frantically]
xd.gif
vchris
I currently don't use any database. There are so little logins that I simply have them hard coded in the page. I'm not sure I want to create one.
vchris
I noticed that in IE6 (don't have IE7 at work) instead of prompting to download the doc filename, it prompts to download the name of the page that gets the doc (doc_fetcher, no extension) but I can still open this word doc with word manually. Any idea why?

EDIT: my bad, typo in content-type for word files.
vchris
I had a meeting today on this application and it looks like they wanna complicate the app a little bit. Currently, I have this site setup without a DB but with the new updates I'll need to implement a database. This way I can easily manage user groups, documents and more from the DB. I'll also be creating an admin panel for users to add, del, edit docs and add, del, edit categories docs are in... Anyway so it looks like this will be another small challenge. Should be interesting.
Skemcin
Sounds fun. Let me know if you need any help on any issues you run into.

You know, with MS SQL 2005 and CF8 you can actually store the binary information in the DB. Not a good idea if this is gonna get really huge or if the files are big, but it is a way to really make sure no one can see someone elses files since they are not even physically stored on the server.
vchris
Actually there should be lots of files, some 5-10mb and more.
Skemcin
QUOTE (vchris @ Jan 9 2008, 09:07 AM) *
Actually there should be lots of files, some 5-10mb and more.

ok, scratch that last idea then - would have been good fro what it looked like you were dealing with at first. Knowing that you are dealing with that many files, I wonder how they expected for it to be managed without a db.

Anyway, good luck, should be a fun little project.
CloneTrooper9494
If I were you, I would use the include, but have a cookie, with the username,country(to block or allow files),password, and a random number like day x hour x 57375(the users id number, like on here) x another number, so somebody would have to at least know one of the numbers to look at the file... does that make any sense?
vchris
I've started to work on this project again. I have the database setup with no data at the moment. What is the best way to have a unique id that I would generate myself when inserting a new document with a coldfusion page? It should have letters (upper and lower case) and numbers, 10 characters should be enough. I'm thinking or an array with all the values I'd want in this id but it's perhaps not the best way of doing this.
Skemcin
ColdFusion has a GUID function that will create a unique identifier for you. If its not going to be used in a visual sense at all (like in the filename or folder name) then I'd go that route. Otherwise, I tend to incorporate a text file and autoincrement that and then prepend it with an abbreviation of the application and or suffix with a version . . .

NW0005v3

NW = news application
00005 = number extracted from a text file with numberformat function used to make sure all four place holders used
v3= the version of the application I built
vchris
I wanna use Random numbers (6 digits) as a unique id for each document in the db. I don't want to have a number the same as one already entered. I'm just trying to figure out how I can reroll the number if there is a match. I know that I query for the ids and set the random number. Then in a loop I compare my random number to all ids and if there is a match it should reenter that loop but before that it should reroll. I'm not sure how to go about that. Any tips?
Skemcin
Taking a minute to throw something out there to start you off . . .
CODE
<cfset newID = randrange(100000,999999)>
<cfquery name="qryCheckID" datasource="#db#">
    SELECT    ID
    FROM        yourtable
    WHERE    ID = newID
</cfquery>
<cfif qryCheckID.recordcount EQ 0>
(use this id)
<cfelse>
(refresh the page, include query string or post back using hidden form)
</cfif>

Does this help?
vchris
It's when it comes to this:
<cfelse>
(refresh the page, include query string or post back using hidden form)
</cfif>

How can I reroll without refreshing the page? cfloop?
Skemcin
Well,

I guess you could nest that in a cfloop like this:
CODE
<cfset newID = randrange(100000,999999)>
<cfloop from="1" to "999999999999999999999999" index="x">
<cfquery name="qryCheckID" datasource="#db#">
    SELECT    ID
    FROM        yourtable
    WHERE    ID = newID
</cfquery>
<cfif qryCheckID.recordcount EQ 0>
(use this id)
<cfbreak>
<cfelse>
<cfset newID = randrange(100000,999999)>
</cfif>
</cfloop>

Here you loop from one to some ridiculously large number. each time a new random number will be created and checked. As soon as there is no match, the loop is broken/exited.

I think this will work.
vchris
Thanks! Seems to work great.
Skemcin
nice
xd.gif
vchris
I got another one for you lol... My documents are stored in the db and there's different categories for each document, some are in the same category. So here's what I got:
Category 1
doc 1
Category 2
doc 2
Category 2
doc 3

I'd like to have:
Category 1
doc 1
Category 2
doc 2
doc 3

I tried using the GROUP BY clause but doc 3 disappeared. I tried also the group in the cfoutput tag but it's the same thing. I checked another document that has a comparable output but it uses a flag to know if it should output the category text. Is that the best way to do this? Isn't there a way with the query to do this?

CODE
<cfquery name="qryGetDocs" datasource="#dbname#" username="#dbuser#" password="#dbpw#">
SELECT d.doc_id, d.doc_name, d.doc_description, c.cat_name
FROM Documents d, Categories c
WHERE d.cat_id = c.cat_id
GROUP BY d.cat_id
</cfquery>

<cfoutput query="qryGetDocs" >
<h3 class="menuheader expandable">#qryGetDocs.cat_name#</h3>
<p>#qryGetDocs.doc_name#</p>
</cfoutput>
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2009 Invision Power Services, Inc.