Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Elliott Sprehn
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Elliott Sprehn

Serving Secure Files With CFContent Tag's File Attribute In ColdFusion

By
Published in Comments (2)

The other day, when considering which files live inside the wwwroot folder on the ColdFusion server, I mentioned that "secure files" live outside the wwwroot folder; but, can be made accessible to the user via ColdFusion. There are many ways to do this; but, perhaps the easiest way is to use the CFContent tag's file attribute. This attribute allows any physical file on your server to be sent to the user, regardless of where it lives.

For this demo, imagine that our ColdFusion application has a path mapping to a non-public folder where all of our secure files are stored:

/secure-files

Since these files live outside the wwwroot folder, the user cannot access them directly. However, when the user provides a correct password, we're going to dynamically serve one of the secure files to the user via the CFContent tag. Additionally, we're going to use the CFHeader tag / Content-Disposition to "change the name" of file as we stream it. This way, we can have a database-driven filename on the server while still providing a user-friendly filename for the download.

<cfscript>

	// Setup form value defaults.
	param name="form.submitted" type="boolean" default=false;
	param name="form.password" type="string" default="";

	// If the form has been submitted and the password is correct, send the file to the
	// user via the CFContent tag.
	if ( form.submitted && ! compare( form.password, "letmein" ) ) {

		// The name of our super secret secure file stored in a non-public folder. We're
		// going to use the CFContent tag to stream this non-public file to the user.
		secureFilename = "0112c97f582d546e39d3547631ef6e85.jpg";
		// The user-friendly name of the file as it will appear when downloaded.
		clientFilename = "lucy.jpg";

		cfheader(
			name = "Content-Disposition",
			value = "attachment; filename=""#clientFilename#""; filename*=UTF-8''#urlEncodedFormat( clientFilename )#"
		);
		cfcontent(
			type = "image/jpeg",
			file = expandPath( "/secure-files/#secureFilename#" )
		);

	}

</cfscript>
<!--- ------------------------------------------------------------------------------ --->
<!--- ------------------------------------------------------------------------------ --->
<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />
</head>
<body>

	<h1>
		Please enter password to download secure file
	</h1>

	<form method="post">
		<input type="hidden" name="submitted" value="true" />
		<input type="text" name="password" value="" />
		<button type="submit">
			Download file
		</button>
	</form>

</body>
</html>

In this case, the CFHeader tag is actually more complicated than it needs to be for the demo. I have it setup to serve filenames with extended UTF-8 characters, even though my secure file only has simple ASCII characters in it.

But, the CFHeader tag is only adjusting the filename being presented to the user - the CFContent tag is doing the real work; its file attribute points to our non-public /secure-files directory, where it is dynamically streaming the private file to our user. As such, when the user enters the correct password, they have the following experience:

A non-public file being downloaded by a user once the user has entered the correct password in a web form.

As you can see, the non-public file is streamed to the user with a new filename.

ASIDE: In this demo we're keeping the secure file in place since it's not user-specific. However, if we were dynamically generating a file on-the-fly and wanted to delete it after ColdFusion was done streaming it, we could include the deleteFile attribute on the CFContent tag.

The file attribute is only one way for the CFContent tag to stream data - it also provides the variable attribute for streaming any binary value to the user. In fact, if you look at my previous post on proxying Gravatar images through ColdFusion for better caching, I use both the file attribute and the variable attribute in order server up avatar images from different sources.

The CFContent tag is just one of those ColdFusion features that makes web application development with CFML really convenient.

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

Great post!

I found out something interesting that I was pulling my hair out for quite some time on: I grabbed some source from another site that did a cfcontent... and cfheader name="Content-disposition". The file downloaded fine, but the filename was always the name of the ColdFusion source file, not the filename specified in the cfheader tag.

I finally realized that the order matters: cfheader should come before cfcontent.

15,841 Comments

@Russ,

Oh yeah, after the CFContent tag is executed the rest of the request is aborted, so anything you want to run must go before that tag. Sorry you ran into that problem, there's no really much to go on - no error or warning or anything like that. Glad you got it figured out!

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel