Accepting file uploads is another common requirement for web applications, but also pose a great risk to both the server and the users of the web application. If not handled correctly, an uploaded file can lead to a compromised server or spread a virus infected file to other users.
The default behavior of the file upload should be to delete the file if it does not pass a validation check. When the file has passed all the checks, move it to the proper location using a system generated file name.
The first and most important thing is that files should NEVER be
uploaded to a web accessible directory. They should always be placed in
a temporary location, generally the ColdFusion temporary directory from
GetTempDirectory()
. On UNIX systems should also restrict access to the
uploaded file by specifying the mode attribute, preferably 600 so that
only the ColdFusion process can read or write to the file.
The types of files accepted in the upload should always be limited
through the ACCEPT
attribute and not allow all file types. There were
several changes to cffile action="upload"
in ColdFusion 10+ on how it
handles what file types are allowed. In previous versions, the ACCEPT
attribute only allowed for a list of mime types like
"image/jpeg,image/png,application/pdf"
in ColdFusion 10+ you can use a
list of file extensions like "*.jpg,*.png,*.pdf"
. You should not mix
the two in the attribute; use one or the other.
Also new in ColdFusion 10 is the strict
attribute which defaults to
true
. In previous versions of ColdFusion, the mime type (content-type
and content-subtype) were based upon what the client told ColdFusion the
file is, not the actual contents. It was possible to check specific file
types by using IsPDFFile()
, IsImageFile()
, or IsSpreadsheet()
, but that
left out a lot of other valid files that could not be checked. With
strict set to true, the mime type of the file is checked when the file
upload occurs; however, this means that ACCEPT must be a list of mime
types and not file extensions. If ACCEPT has a list of file extensions
and strict is set to true, ColdFusion will throw an error.
ColdFusion 10 introduced a new function, FileGetMimeType(), which can now return the mime type for any file.
<cfscript>
variables.validMimeTypes = {
'application/pdf': {extension: 'pdf', application: 'Adobe Acrobat'}
,'application/vnd.ms-powerpoint': {extension: 'ppt', application: 'PowerPoint (97-2003)'}
,'application/vnd.openxmlformats-officedocument.presentationml.presentation': {extension: 'pptx', application: 'PowerPoint (2007)'}
,'image/jpeg': {extension: 'jpg'}
,'image/png': {extension: 'png'}
};
</cfscript>
<cftry>
<cffile action="upload" filefield="uploadFile"
destination="#GetTempDirectory()#" mode="600"
accept="#StructKeyList(variables.validMimeTypes)#"
strict="true"
result="request.uploadResult"
nameconflict="makeunique">
<cfcatch type="any">
<!--- file is not written to disk if error is thrown --->
<!--- prevent zero length files --->
<cfif FindNoCase( "No data was received in the uploaded", cfcatch.message )>
<cfabort showerror="Zero length file">
<!--- prevent invalid file types --->
<cfelseif FindNoCase( "The MIME type or the Extension of the uploaded file", cfcatch.message )>
<cfabort showerror="Invalid file type">
<!--- prevent empty form field --->
<cfelseif FindNoCase( "did not contain a file.", cfcatch.message )>
<cfabort showerror="Empty form field">
<!---all other errors --->
<cfelse>
<cfabort showerror="Unhandled File Upload Error">
</cfif>
</cfcatch>
</cftry>
<!--- get actual mime type --->
<cfset variables.actualMimeType = FileGetMimeType( request.uploadResult.ServerDirectory & '/' & request.uploadResult.ServerFile, true )>
<!--- redundant check with strict="true", does not hurt to double check Adobe --->
<cfif NOT StructKeyExists( variables.validMimeTypes, variables.actualMimeType )>
<cffile action="delete" file="#request.uploadResult.ServerDirectory#/#request.uploadResult.ServerFile#" >
<cfabort showerror="Invalid file type (checked)">
</cfif>
<!--- generate unique filename for move to destination, DO NOT reuse filename sent by user --->
<cfset request.uploadFile.destination = ExpandPath( "/" ) & "uploads/" & CreateUUID() & "." & variables.validMimeTypes[variables.actualMimeType]["extension"]>
<cffile action="move"
source="#request.uploadResult.ServerDirectory#/#request.uploadResult.ServerFile#"
destination="#request.uploadFile.destination#" mode="644">
ColdFusion 11 Update 18, ColdFusion 2016 Update 10 and ColdFusion 2018 Update 3 add support for a new Application.cfc
and ColdFusion Administrator Setting:
this.blockedExtForFileUpload="*";
By setting it to *
we are telling ColdFusion that this application should not accept file uploads at all. If your application does allow uploads you should set it to a list of file extensions that the server treats as executable (cfm,cfc,jsp
etc.)
If possible limit file uploads to only authenticated users of the web application.
What is not shown through the code sample above is processing the upload through any type of virus scanner or any additional file size checks that could be done beyond the post limit size set in ColdFusion Administrator or through the web server configuration.
Also, additional restrictions could be put in place using OS level permissions and/or using Sandboxing within ColdFusion to limit where ColdFusion can read and write the file.