How can I confirm a cffile write is successful? - coldfusion

I am using cffile to create a new file or update an existing file, depending on what the user requested. The request comes through a form from the previous procedure, so the code involving cffile looks like this:
<cfset thefile = "#form.dyn#">
<cfoutput>
<cfsavecontent variable = "testvar">
#form.editor1#
</cfsavecontent>
<cffile action = "write"
file = "/var/www/reports/#thefile#.cfm"
output = "#testvar#">
</cfoutput>
When I am done writing to the file, I want to confirm to the user that this happened. For a new file I could use IsDefined to check that it is there. But I can't think of a way to check for an existing file that was updated. I considered a try/catch on the cffile, but the catch operates only if nothing seems to go wrong. If I don't get an error on the catch, can I assume everything is all right? I would prefer a direct check if possible. Does anyone have an idea?

If <cffile> doesn't work, it'll tell you by throwing an exception. If it doesn't do that, you can safely assume it has worked. Don't over-engineer your app.

You could use cfdirectory with the action="list" and filter="your-filename" to get the following information about the uploaded file:
If action = "list", cfdirectory returns the following result columns, which you can reference in a cfoutput tag:
name: Directory entry name. The entries "." and ".." are not returned.
directory: Directory that contains the entry.
size: Directory entry size.
type: File type: file, for a file; dir, for a directory.
dateLastModified: The date that an entry was last modified.
attributes: File attributes, if applicable.
mode: Empty column; retained for backward compatibility with ColdFusion 5 applications on UNIX.
Of interest to you is the dateLastModified column.
So you should be able to do something like:
<cfdirectory action="list" name="dirQuery" directory="C:/var/www/reports/" filter="#thefile#.cfm">
Then you can dump that result to see what information is available to you:
<cfdump var="#dirQuery#">
The dateLastModified column can be accessed like:
<cfoutput>#dirQuery.dateLastModified#</cfoutput>

Use CFDirectory to get the file's dateLastModified before you update the file and then again afterwards. If they are not the same, then it was updated.

Related

ColdFusion CFFILE to limit text file upload

I'm want to use CFFILE upload to detect only .txt file. I've tried to use file.clientfileext to detect the extension. When TXT is detected, I'm showing a pop up error message to users and delete the file. But I was told I should not even allow user's file to reach our server.
Then I use CFFILE accept attribute to only accept text/plain. This should do it but unfortunately on my test when I tried uploading non text file I got ColdFusion error:
The MIME type of the uploaded file application/pdf was not accepted by
the server. Only files of type text/plain can be uploaded. Verify
that you are uploading a file of the appropriate type.
I tried to use cftry and cfcatch but I still get the same error, this mainly due to the MIME Type that I don't know when the file is being uploaded by the browser.
I also found the same question in this forum and tried the suggested answer, it did not work, still got the same error message (see below)
I also found another posting in this forum that do not suggest the use of CF "accept" attribute. This link is provided for a further detail explanation: http://www.petefreitag.com/item/701.cfm
So my question is, since I'm still using CF8, I actually don't have many options to prevent my users from uploading other than .txt file securely?
If I can't use the accept attribute of the CFFILE, can I at least secure my file upload functionality by doing the following? but is doing it this way safe enough?
Upload the file to a temp folder that is not under the root dir
verify the file extension
change the file name even if the extension is detected to be a .txt
move the file to the destination file under the root dir
Even if I do these steps, I have to allowed the file to reach our server, the order is to NOT allow the file to reach our server.
Below is the answer/suggestion from previous question. But it doesn't work when I tested it:
<CFTRY>
<cflock name="write_lock" type="Exclusive" timeout="120">
<cffile action="upload" filefield="filepath" destination="#DestDir#"
nameconflict="Overwrite" attributes="Archive">
</cflock>
<CFCATCH>
<cfif FindNoCase("not accepted", cfcatch.Message)>
<script>
$(function(){
alert("Only the following file types are allowed: .jpg, .gif, .bmp,
.png.");
});
</script>
<cfabort />
<cfelse>
<!--- looks like non-MIME error, handle separately --->
<cfdump var="#cfcatch#" abort />
</cfif>
</CFCATCH>
</CFTRY>
I think your steps are reasonable if you don't like using the Accept attribute for validation. FYI you can set accept to .txt instead of the MIME types. The MIME type was determined by the client so it's safer to check the extension anyway.
The exception thrown by cffile failing attribute validation may not have a type, so the code you posted tried to detect it with FindNoCase() by looking at the exception's message. You can dump the exception out and find out why the FindNoCase() failed to catch the exception.
Make sure you treat whatever uploaded as something potentially malicious and do not process them (e.g. cfinclude them). Forcing the file extension to be .txt should be safe enough, but I'll let other security experts charm in.
You can use the below code:
<cffile action="upload" filefield="BidDoc"
destination="C:\upload\"
nameconflict="makeunique"
accept="text/plain">
The other mime types which you may use are:
application/pdf
application/msword
application/vnd.ms-excel
application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
As discussed in this answer, there really is no 100% fool-proof way.
If you don't want to trust the "accept" attribute, I would suggest allowing the user to upload the file and then checking the mime type of the uploaded file using the cffile.contentType property. Check against whatever mime types you wish to allow/restrict and reject with the appropriate message. You may also choose to employ a check of the file extension as an added layer of error checking.
It must be noted that like file extensions, mime types can not be 100% trusted to be accurate as they can be edited by the user. But using a combination of checks you can be reasonably that most files uploaded are of the correct type.
Coldfusion will not prevent a file from being uploaded to a server. You can set a maximum file size but this is processed during the upload. The cffile tag kicks in after the file is uploaded. Furthermore it is rather difficult to really determine if a file is a text file or a jpg, exe, rar etc file. The following q & a may help:
Determining binary/text file type in Java?
In my opinion it is best to follow the tips given by pete freitag and use a java class to determine the file type. Then you can delete all non text files.

How to get directory path up to a specific folder only?

Using: CF10 and IIS7.5
I have a section within my website called "Bookings". It is located like this:
c:\inetpub\wwwroot\mysite\bookings
Within this folder will be sub-folders and eventually webpages themselves. Heres an example:
c:\inetpub\wwwroot\mysite\bookings\holidays\new.cfm
c:\inetpub\wwwroot\mysite\bookings\carhire\edit.cfm
I include (using <cfinclude>) another page within each webpage that displays different links depending on which page is calling it. All I want to know is the directory up to the "bookings" folder. Something like this (pseudo code):
<cfset whereAmI = #GetDirectoryFromPath(GetBaseTemplatePath())#>
<cfif #whereAmI# EQ "C:\inetpub\wwwroot\mysite\bookings">
<h1>Booking Section Links</h1>
</cfif>
The above code works only if the user visits the bookings/index.cfm page of the "bookings" folder. But if they go to the bookings/holidays/new.cfm page, it is now in the holidays folder so the <h1> content will not appear. I really only want to check for any page that is in the bookings folder, even if it is within a subfolder within the bookings folder. A bit like in SQL where I could say IF #GetDirectoryFromPath(GetBaseTemplatePath())# LIKE 'c:\inetpub\wwwroot\mysite\bookings%' so it has a wildcard on the end.
I know this question is going to irritate the MVC framework advocates but please excuse me on this!
Here is a quick, easy way to solve your problem (may not work as system expands - but should get you started down the right path).
<cfset whereAmI = GetDirectoryFromPath(GetBaseTemplatePath())>
<cfif whereAmI CONTAINS "C:\inetpub\wwwroot\mysite\bookings">
<h1>Booking Section Links</h1>
</cfif>
Note, I removed the # from inside the cfset and cfif you do not need them there.
You could even scale back the path to use just 'mysite\bookings'.
Ideally, this should be wrapped up into a function so that you can easily pass different paths into it to determine if you are on a given page. Or, possibly, even determine the 'parent' folder in onRequestStart in Application.cfc and set it as a request scope variable.
This will need to be tweaked if you run the code on a *nix based system.
It is more easier with CGI variables. You can use "CF_TEMPLATE_PATH". Try this
<cfoutput>The value of CF_TEMPLATE_PATH is: </cfoutput><cfdump var="#CF_TEMPLATE_PATH#">

CFinclude issue

I am trying to cfinclude a file and also pass some values to that specific file. I don't know if it is possible so any reply is appreciated.
This is my code:
<cfoutput>
<cfset path = "?id=#email_id#&contactid=#email_contactid#&ownerid=#email_ownerid#">
<cfinclude template="/email_results#path#">
</cfoutput>
My mapping goes something like this:
E:\sites\exampleCom\cf_modules\reports\resultsTemplate.cfm
I did a #expandPath("/email_results#path#")# and it is giving me the correct output
(E:\sites\exampleCom\cf_modules\reports\resultsTemplate.cfm?id=123&contactid=123&ownerid=123)
but when I am trying to include the file it bugs out. Can I pass in variables through the cfinclude? Is this what it bugs out my code?
No, you do not pass variables to an include page. The include will be execute in the same context, so it can directly see the email_id and other variables.

Does <cffile> "write" allow a file to execute before renaming/writing the file?

I've created a file upload for my client and I'm trying to make things as secure as possible. I'm using the following code to handle the file upload. The idea is to rename the file and write it to a folder outside the web root.
The question is, during the 'write' process is there any chance that ColdFusion will allow a malicious file to execute before the file is written to the folder and renamed with the following code?
This is at the top of my component...
<cfset destdir = "/folder/upload/">
This is part of the code that handles the file...
<cfset var local = structNew()>
<cfset local.response = structNew()>
<cfset local.response['catcher'] = ''>
<cfset local.filename = listGetAt(#arguments.file#, 1, ".")>
<cfset local.fileext = ListLast(#arguments.file#, ".")>
<cfset local.nfile = #CreateUUID()# & "." & #local.fileext#>
<cftry>
<cffile action="write" file="#destdir##local.nfile#" output="#arguments.content#">
<cfset local.response['newfilename'] = local.nfile>
<cfcatch type="any">
<cfset local.response['catcher'] = "Write Exception " & #cfcatch.Detail# & " | " & #cfcatch.Message#>
<cfset local.response['success'] = true>
<cfreturn local.response>
</cfcatch>
</cftry>
I should mention that the file upload procedure is being handled by a CFC and Valums' AjaxUpload Plugin...
Your sample code looks like you're doing something different from a normal file upload. You don't have a cffile action="upload", and it looks like you're already retrieved the content of the file. You should be limiting the local.fileext to file types that you consider safe and the Arguments.content should be checked to make sure it's not malicious. As soon as you've written the file to the webroot it can be probed through the url, so you must verify that everything is safe before writing it.
With a normal form post file upload the process should be something like:
Use cffile action="upload" to write the file upload into a temp folder outside the webroot
Verify the integrity of the file to make sure it's not malicious (delete if bad file)
Move the verified file into its final location with cffile action="move"
To answer the question you asked - your "write" opertation is a single operation. You are not moving and renaming the original file (at least not in the code above). Instead you are creating a file handle, outputting a buffer and closing the handle. The code cannot be executed prior to the release of the handle. If you were moving and renaming or copying the file itself then there could be a gap as you fear - enough to allow an execution. You should also know that file I/O might create problems if you intend to write then execute the file in a single request thread (could get an error trying to get access to the file as Java might hit slightly ahead of the OS on getting notice of the handle release if you see what I'm saying).
Here's a post on cffile hacking that might shed light around the edges of your issue.
http://www.coldfusionmuse.com/index.cfm/2009/9/18/script.insertion.attack.vector
Note - this is my understanding... pretty solid, but there some pretty smart folks on this list including the ones who have responded already. Not trying to steal anyone's thunder here.
Renaming the file at upload and placing in outside the web root is a good idea, but there are still some basic points how you can improve security at fileupload in coldfusion.
For starters cffile with action upload has an attribute "accept" where you can specify which mime-types (comma delimited list) your fileupload will allow.
cffile also has a "mode" attribute (for linux only) to set permissions for the file.
source: http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=Tags_f_02.html
I don't think that an uploaded malicious file can be executed automaticly when it's uploaded that easy, but it's a good practice to take precautions.

<cffileupload> and Database Insert of File Name

I am having a problem with the cffileupload tag and then inserting the names of the files uploaded into the database. It seems like there are no hooks back from the CF Action page that would say if it was successful, if the file was renamed, what the new filename is, and
Basically, I want a user to be able to upload mulitple files (up to 10) without a timeout and storing the name of the files in the database.
Any suggestions would be welcome! I've dug around on the interweb, and there doesn't seem to be any elegant solutions.
After the file is processed by <cffile action="upload">, there's a huge amount of information available via the variable specified in "result". This includes the original filename, the file size, and an indicator of success. Adobe's docs on cffile has the details. Your best bet is to create a temporary file, push the upload to there via cffile, evaluate success or failure, then push it into the database. Something like:
<cfset TemporaryFile = GetTempFile(GetTempDirectory(), "myAppTemp") />
<cffile action="upload" destination="#TemporaryFile#"
fileField="filefield" result="FileResults" />
Then you can access your file via the TemporaryFile variable, and the filename via FileResults.clientFile. FileResults.fileWasSaved should indicate success or failure of the upload and subsequent save to disk.