Skip to content

Instantly share code, notes, and snippets.

@timsayshey
Forked from rip747/.gitignore
Created August 10, 2016 18:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timsayshey/7950f8b3e7f2bebe6e1d97532be6bdcd to your computer and use it in GitHub Desktop.
Save timsayshey/7950f8b3e7f2bebe6e1d97532be6bdcd to your computer and use it in GitHub Desktop.

#CFFileUpload ##Replacement for CFFILE to prevent the MIME/FILE upload security vulnerability

Security Issue as documented at:

http://www.coldfusionjedi.com/index.cfm/2009/6/30/Are-you-aware-of-the-MIMEFile-Upload-Security-Issue

This is basically a drop in UDF to replace your tag. What it will do is intercept the destination directory passed to it and upload the file to CF temp directory using getTempDirectory(). It then performs some checks to make sure that the file uploaded is of the correct MIME type through the file extension and a list of known MIME types for that extension. If everything checks out, it will MOVE the file to the desired destination. If not then it will throw an error that you can catch. You also have the option of deleteing the invalid file from the temp direcotry (which is the default) or keeping it so you can inspect it later (which might be good).

I've included some of the most popular MIME types that people upload like office documents, images, and pdfs as default checks. You can add to this list or overwrite it by passing your own value with the mimeTypes argument.

Lastly, I've included a list of invalid file extensions that you can append to by passing in your own list with the badExtensions arguments. Note that the you can't override the internal list, only append it.

###Instead of:

<cffile
	action="UPLOAD"
	filefield="form.thefile"
	destination="#expandpath('.')#"
	nameconflict="MAKEUNIQUE">
<cfset form.thefile = cffile.serverfile>

###You replace with:

<!--- In this example I cached all my UDF into a struct called functions within the application scope --->
<cfset myfile = application.functions.cffileupload(
	,filefield="form.thefile"
	,destination="#expandpath('.')#"
	,nameconflict="MAKEUNIQUE"
	)>
<cfset form.thefile = myfile.serverfile>

##Advanced Features

Besides being a dropin replacement, CFFileUpload also allows you to do some advanced stuff as well such as:

  • You can append the list of bad extensions to further lock down your application
  • You can add additional mimetypes or overload the already supported ones
  • You can allow a custom extension to be valid

##Arguments

###deleteBadFile (boolean)

Set to true by default. Tells the function to delete the uploaded file from the directory when it throws an error.

By default any invalid files will be deleted from the ColdFusion Temp directory. By setting this argument to false the files will be kept. This can be used to analyze what security attempts are being used to target the server.

###badExtensions (string)

Blank by default. Appends a list of extensions to the internal extensions list that shouldn't be uploaded.

To prevent malicious files from being uploaded, CFFileUpload maintains an internal list of extensions that cannot be uploaded. This internal list CANNOT be overwritten, however it can be appended to include other extensions that the user wants to prevent from being uploaded. The following extension are in this internal list:

cfm,cfml,cfc,dbm,jsp,asp,aspx,exe,php,cgi,shtml

###mimeTypes (struct)

Empty by default. Allows to extend or replace the internal list of MIME types.

In order for an extension to have permission to be uploaded it must have a MIME type registered with it. When a file is uploaded, CFFileUpload tries to determine MIME type by checking the registered MIME types on the server using:

getPageContext().getServletContext().getMimeType()

If it finds that the MIME type is registered it will use make sure that the extension of the file uploaded matches the MIME types associated with the extension from the internal mimetypes structure. If the file's extension doesn't match an associated MIME type, it is invalid.

However sometimes an extension must be allowed to be uploaded even though the MIME type is not registered on the server. This could be because the site is in a shared hosting environment or has other restrictions. By appending the internal list with custom extensions and MIME types, you can allow for these files to be uploaded.

The default list of MIME types are as follows:

<!--- pdf --->
<cfset loc.mimetypes.pdf = "application/pdf,application/x-pdf,app/pdf">
<!--- office --->
<cfset loc.mimetypes.ppt = "application/vnd.ms-powerpoint">
<cfset loc.mimetypes.xls = "application/vnd.ms-excel">
<cfset loc.mimetypes.doc = "application/msword">
<cfset loc.mimetypes.pub = "application/x-mspublisher,application/vnd.ms-publisher">
<cfset loc.mimetypes.docm = "application/vnd.ms-word.document.macroEnabled.12">
<cfset loc.mimetypes.docx = "application/vnd.openxmlformats-officedocument.wordprocessingml.document">
<cfset loc.mimetypes.dotm = "application/vnd.ms-word.template.macroEnabled.12">
<cfset loc.mimetypes.dotx = "application/vnd.openxmlformats-officedocument.wordprocessingml.template">
<cfset loc.mimetypes.potm = "application/vnd.ms-powerpoint.template.macroEnabled.12">
<cfset loc.mimetypes.potx = "application/vnd.openxmlformats-officedocument.presentationml.template">
<cfset loc.mimetypes.ppam = "application/vnd.ms-powerpoint.addin.macroEnabled.12">
<cfset loc.mimetypes.ppsm = "application/vnd.ms-powerpoint.slideshow.macroEnabled.12">
<cfset loc.mimetypes.ppsx = "application/vnd.openxmlformats-officedocument.presentationml.slideshow">
<cfset loc.mimetypes.pptm = "application/vnd.ms-powerpoint.presentation.macroEnabled.12">
<cfset loc.mimetypes.pptx = "application/vnd.openxmlformats-officedocument.presentationml.presentation">
<cfset loc.mimetypes.xlam = "application/vnd.ms-excel.addin.macroEnabled.12">
<cfset loc.mimetypes.xlsb = "application/vnd.ms-excel.sheet.binary.macroEnabled.12">
<cfset loc.mimetypes.xlsm = "application/vnd.ms-excel.sheet.macroEnabled.12">
<cfset loc.mimetypes.xlsx = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
<cfset loc.mimetypes.xltm = "application/vnd.ms-excel.template.macroEnabled.12">
<cfset loc.mimetypes.xltx = "application/vnd.openxmlformats-officedocument.spreadsheetml.template">
<!--- images --->
<cfset loc.mimetypes.jpg = "image/jpg,image/pjpg,image/jpeg,image/pjpeg">
<cfset loc.mimetypes.jpeg = "image/jpg,image/pjpg,image/jpeg,image/pjpeg">
<cfset loc.mimetypes.gif = "image/gif">
<cfset loc.mimetypes.png = "image/png">
<cffunction name="CFFileUpload" returntype="struct" access="public" output="false">
<cfargument name="deleteBadFile" type="boolean" required="false" default="true" hint="should we delete an invalid files that are uploaded from the temp directory.">
<cfargument name="badExtensions" type="string" required="false" default="" hint="appends the internal extension list. any file with these extensions is automatically invalid.">
<cfargument name="mimeTypes" type="struct" required="false" default="#structnew()#" hint="appends or replace the internal list of mimetypes with your own custom list.">
<cfargument name="allowExtensions" type="string" required="false" default="" hint="extensions that don't have a mimetype associated with them, but should be allowed.">
<cfset var loc = {}>
<cfset loc.badExtensions = "cfm,cfml,cfc,dbm,jsp,asp,aspx,exe,php,cgi,shtml">
<cfset loc.mimetypes = {}>
<!--- pdf --->
<cfset loc.mimetypes.pdf = "application/pdf,application/x-pdf,app/pdf">
<!--- office --->
<cfset loc.mimetypes.ppt = "application/vnd.ms-powerpoint">
<cfset loc.mimetypes.xls = "application/vnd.ms-excel">
<cfset loc.mimetypes.doc = "application/msword">
<cfset loc.mimetypes.pub = "application/x-mspublisher,application/vnd.ms-publisher">
<cfset loc.mimetypes.docm = "application/vnd.ms-word.document.macroEnabled.12">
<cfset loc.mimetypes.docx = "application/vnd.openxmlformats-officedocument.wordprocessingml.document">
<cfset loc.mimetypes.dotm = "application/vnd.ms-word.template.macroEnabled.12">
<cfset loc.mimetypes.dotx = "application/vnd.openxmlformats-officedocument.wordprocessingml.template">
<cfset loc.mimetypes.potm = "application/vnd.ms-powerpoint.template.macroEnabled.12">
<cfset loc.mimetypes.potx = "application/vnd.openxmlformats-officedocument.presentationml.template">
<cfset loc.mimetypes.ppam = "application/vnd.ms-powerpoint.addin.macroEnabled.12">
<cfset loc.mimetypes.ppsm = "application/vnd.ms-powerpoint.slideshow.macroEnabled.12">
<cfset loc.mimetypes.ppsx = "application/vnd.openxmlformats-officedocument.presentationml.slideshow">
<cfset loc.mimetypes.pptm = "application/vnd.ms-powerpoint.presentation.macroEnabled.12">
<cfset loc.mimetypes.pptx = "application/vnd.openxmlformats-officedocument.presentationml.presentation">
<cfset loc.mimetypes.xlam = "application/vnd.ms-excel.addin.macroEnabled.12">
<cfset loc.mimetypes.xlsb = "application/vnd.ms-excel.sheet.binary.macroEnabled.12">
<cfset loc.mimetypes.xlsm = "application/vnd.ms-excel.sheet.macroEnabled.12">
<cfset loc.mimetypes.xlsx = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
<cfset loc.mimetypes.xltm = "application/vnd.ms-excel.template.macroEnabled.12">
<cfset loc.mimetypes.xltx = "application/vnd.openxmlformats-officedocument.spreadsheetml.template">
<!--- images --->
<cfset loc.mimetypes.jpg = "image/jpg,image/pjpg,image/jpeg,image/pjpeg">
<cfset loc.mimetypes.jpeg = "image/jpg,image/pjpg,image/jpeg,image/pjpeg">
<cfset loc.mimetypes.gif = "image/gif">
<cfset loc.mimetypes.png = "image/png">
<!--- append our mime types with any custom ones --->
<cfif not structisempty(arguments.mimeTypes)>
<cfset structappend(loc.mimetypes, arguments.mimeTypes, true)>
</cfif>
<cfset structdelete(arguments, "mimeTypes")>
<!--- append our badExtensions with any custom ones --->
<cfset loc.badExtensions = listappend(loc.badExtensions, arguments.badExtensions)>
<cfset structdelete(arguments, "badExtensions")>
<!--- the result to use internally --->
<cfset arguments.result = "loc.cffile">
<!---
intercept the upload destination and set it to the tempdirectory,
save it so we can move the file later
--->
<cfset loc.destination = arguments.destination>
<cfset arguments.destination = getTempDirectory()>
<!--- execute upload --->
<cffile attributeCollection="#arguments#">
<!--- get the full filename that was uploaded --->
<cfset loc.fileuploaded = arguments.destination & loc.cffile["serverFile"]>
<!--- try to get the mimetype of the uploaded file --->
<cfset loc.filemimetype = getPageContext().getServletContext().getMimeType(loc.fileuploaded)>
<cfif
listfindnocase(loc.badExtensions, loc.cffile["serverFileExt"])
or not structkeyexists(loc.mimetypes, loc.cffile["serverFileExt"])
or (not len(arguments.allowExtensions) && not structkeyexists(loc, "filemimetype"))
or (len(arguments.allowExtensions) && not listfindnocase(arguments.allowExtensions, loc.cffile["serverFileExt"]))
or (structkeyexists(loc, "filemimetype") && not listfindnocase(loc.mimetypes[loc.cffile["serverFileExt"]], loc.filemimetype))>
<cfif arguments.deleteBadFile>
<cffile action="delete" file="#loc.fileuploaded#">
</cfif>
<cfthrow type="Custom" message="Invalid file type">
</cfif>
<!--- full path to move the file to --->
<cfset loc.finaldestination = listappend(loc.destination, loc.cffile["serverFile"], "\/")>
<!--- handle makeunique when moving --->
<cfif structkeyexists(arguments, "nameconflict") and arguments.nameconflict eq "makeunique">
<cfif fileexists(loc.finaldestination)>
<cfset loc.cffile["serverFileName"] = loc.cffile["serverFileName"] & gettickcount()>
<cfset loc.cffile["serverFile"] = listappend(loc.cffile["serverFileName"], loc.cffile["serverFileExt"], ".")>
<cfset loc.finaldestination = listappend(loc.destination, loc.cffile["serverFile"], "\/")>
</cfif>
</cfif>
<!--- forgot to handle mode in unix --->
<cfset loc.args = {}>
<cfset loc.args.action = "move">
<cfset loc.args.source = "#loc.fileuploaded#">
<cfset loc.args.destination = "#loc.finaldestination#">
<cfif structkeyexists(arguments, "mode")>
<cfset loc.args.mode = "#arguments.mode#">
</cfif>
<!--- move the file --->
<cffile attributeCollection="#loc.args#">
<!--- append the mimetype to the returning structure for convinence --->
<cfset loc.cffile.mimetype = listfirst(loc.mimetypes[loc.cffile["serverFileExt"]])>
<cfif structkeyexists(loc, "filemimetype")>
<cfset loc.cffile.mimetype = loc.filemimetype>
</cfif>
<cfreturn loc.cffile>
</cffunction>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment