Avg. Rating 5.0

Problem

Allowing users to upload image files, which are then displayed, can seem extremely simple but has its challenges and security concerns. The main danger is the fact that much of the information available to us at the time of an upload request is supplied by the client/browser, which means all of that information has the potential to be "spoofed" by an unscrupulous hacker.

Solution

We can safely upload image files by uploading to a temporary (non-Web-accessible) directory and then moving beneath the Web root once we're certain we have a safe and valid image file.

Detailed explanation

Allowing users to upload image files, which are then displayed, can seem extremely simple but has its challenges and security concerns. The main danger is the fact that much of the information available to us at the time of an upload request is supplied by the client/browser, which means all of that information has the potential to be "spoofed" by an unscrupulous hacker.

For example, keep in mind that the accept attribute of the cffile tag can only validate the MIME type against that which is supplied by the client/browser. Once again, this can be spoofed and is therefore not to be trusted! The following example is still prone to accepting a .cfm file with a spoofed MIME type of image/gif, for example.

<cffile action="upload" filefield="uploadFile"
destination="#expandPath('images')#" accept="image/gif" />

The following "recipe" is a fairly simple example that demostrates a number of security measures taken upon file upload:

  • Use the accept attribute in cffile to limit MIME types accepted from client; catch potential error thrown.
  • Save uploaded files to a temporary directory that is not accessible by the client/browser.
  • Leverage the isImageFile() function to ensure the uploaded file is an image.
  • Limit the file extensions allowed.
  • Limit the file size allowed.
<cfset imagesDir = "images" />
<cfset imagesPath = expandPath(imagesDir) />
<cfset uploadPath = getTempDirectory() />
<cfset acceptMimeTypes = "image/gif,image/jpeg,image/png" />
<cfset acceptExtensions = "gif,jpeg,jpg,png" />

<!--- Process file upload, if form submitted --->
<cfif structKeyExists(form, "uploadFile")>

    <!--- Catch error if MIME type is not accepted --->
    <cftry>
        <cffile action="upload"
                filefield="uploadFile"
                destination="#uploadPath#"
                accept="#acceptMimeTypes#"
                nameConflict="makeUnique"
                result="uploadResult" />
        <cfcatch type="any">
            <p>ERROR: <cfoutput>#cfcatch.message#<br
/>#cfcatch.detail#</cfoutput></p>
        </cfcatch>
    </cftry>

    <!--- Proceed only if successful upload... --->
    <cfif isDefined("uploadResult")>

        <!--- Ensure that file is an image --->
        <cfif
isImageFile("#uploadPath#/#uploadResult.serverFile#")
                AND listFindNoCase(acceptExtensions,
uploadResult.serverFileExt)
                AND uploadResult.fileSize LTE 102400
        >
   
            <!--- OK to move the file from the temporary
location to the images folder under Web root --->
            <cffile action="move"
source="#uploadPath#/#uploadResult.serverFile#"
destination="#imagesPath#" />
   
            <!--- Display success message, stats and image
--->
            <cfoutput>
                <p>Image uploaded!
(#uploadResult.serverFile#, #numberFormat(
uploadResult.fileSize/1024 )# KB)</p>
                <p><img
src="#imagesDir#/#uploadResult.serverFile#" /></p>
            </cfoutput>
   
        <!--- File upload not allowed! --->
        <cfelse>
   
            <!--- Show error message and delete file from temp
directory --->
            <p>ERROR: You may only upload a 100 KB or less
file of type image with one of the following file extensions:
#replace( acceptExtensions, ",", ", ", "all" )#.</p>
            <cffile action="delete"
file="#uploadPath#/#uploadResult.serverFile#" />
   
        </cfif>

    </cfif>

</cfif>

<!--- Display form to allow user to select an image file to
upload --->
<cfoutput>
    <form action="#cgi.script_name#" method="post"
enctype="multipart/form-data">
        Select image to upload: <input type="file"
name="uploadFile" />
        <input type="submit" value="Upload File" />
    </form>
</cfoutput>

For further reading, Pete Freitag has a great post here on tips for safe file uploads.

safe-image-upload.cfm.zip
[Code example for safe image file upload.]

+
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

Report abuse

Related recipes