Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Scotch On The Rock (SOTR) 2010 (Brussels) with: Cyril Hanquez

Ask Ben: Hiding Customer-Specific Image Paths

By Ben Nadel on

How to hide my folder images? Ben, I have many folders with images for my customers

/customer1/images
/customer2/images
/customern/images
...etc

is the way to hide my path (or create temp path) from url donwload images page? Original Code:

<a href=/customer1/image1.gif>donwload pic</a>

This is critical.. customer change url and get images from another customer.. Hide Path, some like this...

<a href=/ASDE-FDRE-FGRT/image1.gif>donwload pic</a>

Create a field into DB and create virtual path (IIS) is a one solution (ASDE-FDRE-FGRT = customer1) ... is there another solution with cf? For any comments.. thanks

I have never worked much with virtual paths or anything like that because most of the time, I don't have access to that piece of functionality (IIS). Additionally, if this is a situation that is changing regularly (adding or deleting new customers), I tend to like having a very dynamic solution. To me, this means an all-ColdFusion solution. The first thing that popped to mind was piping all image requests through a ColdFusion page that streams the image back to the client.

So, for example, instead of having a URL like:

/customer1/image1.gif

... you could have:

/images/?image1.gif

What this assumes is that there's an "images" directory that has an index.cfm page that is using the query string value (image path) to return the appropriate image to the client. The code for this might look something like:

  • <!---
  • Param the image path. By default, our path is going
  • to be everything past the query string delimiter.
  • --->
  • <cfparam
  • name="URL.image_path"
  • type="string"
  • default="#CGI.query_string#"
  • />
  •  
  •  
  • <!---
  • Get the image directory path based on the current customer.
  • This assumes that the "current" customer is in some sort
  • request-based variable.
  • --->
  • <cfset strImageDirectory = REQUEST.ImageDirectory />
  •  
  • <!---
  • Now, we can get the full path of the image (this is the
  • fully expanded, server path, not a URL path).
  • --->
  • <cfset strFullImagePath = (strImageDirectory & URL.image_path) />
  •  
  •  
  • <!--- Set header values. --->
  • <cfheader
  • name="content-disposition"
  • value="attachment; filename='#ListLast( URL.image_path, "\/" )#'"
  • />
  •  
  • <!--- Stream the image to client. --->
  • <cfcontent
  • type="image/#ListLast( URL.image_path, '.' )#"
  • file="#strFullImagePath#"
  • />

In the above code, you will see that the actual image directory is stored in a REQUEST-scoped variable, REQUEST.ImageDirectory. By doing it this way, we can change this value on a page-request basis. Each user might be a different customer and will therefore have a different REQUEST.ImageDirectory property value. The image, as taken from the query string, is then streamed to the browser using ColdFusion's CFContent tag.

By doing it this way, not only is the location of the image kept hidden, it doesn't even need to be a web-accessible directory. ColdFusion's CFContent tag can stream any file that the server has access to, even if it is not in a public directory.

There are some down sides to using CFContent to stream data back to the client, but as Eric Stevens pointed out, ColdFusion 8 seems to use CFContent quite efficiently. I hope that this suggestion can help in some way, or perhaps provide some inspiration.

Tweet This Groovy post by @BenNadel - Ask Ben: Hiding Customer-Specific Image Paths Thanks my man — you rock the party that rocks the body!


Reader Comments

I do something similar when delivering dynamic documents too. If you're really paranoid, I suppose you could also throw in a cffile to copy the image to a temp dir, but probably wouldn't be necessary.

Since your working with images, I might also throw in a IsImageFile in there too.

<cfif IsImageFile("strFullImagePath")>
<cfheader ... />
<cfcontent ... />
<cfelse>
Error message here
</cfif>

I would be careful of cfcontent - depending on the level of traffic and size of images your are serving it can quickly bring your server grinding to a halt as a thread is consumed until the image transfer is complete.

It just takes a few users with slow connections requesting large images.

@Steve,

Good point. Plus, there would probably need to be some additional path scrubbing to make sure no one tried to stream anything they are not supposed to.

@Johans,

I suppose that as the sites get bigger and more complex, the bigger and more complex your solution needs to be. I would assume that as the site gets more traffic, one might consider getting static file servers and other things that help to alleviate like concerns.

But, yes, one should be cautious.

Hi Guys,

I know CF can do this, but why? It's like using a fork to eat soup. Sure you can do it, but it's slow.

Check out Isapi rewrite / mod_rewrite, if you're using IIS / apache. You can set up rules to 'mask' URLs. If you're using isapi rewrite, the rule in this case would be something like:

RewriteMap user txt:user.txt
RewriteCond %{REQUEST_URI} ^/([^/]+)/?(.*)?$
RewriteRule .? http://www.test.com/${user:%1}/%2 [NC,L,P]

Then in your user.txt file
ASDE-FDRE-FGRT customer1
ASDE-FDRE-FGRG customer2
... etc.

HTH,

Jim

@Jim,

I like the re-write solution, but it seems that the client paths are not obfuscated in any way. Meaning, someone can just start guessing at URLs. By hiding the "current client" in the ColdFusion page request, you can pipe ALL requests to the same place and have the ColdFusion logic determine which client is actually being used.

Ah, I see now. The author is trying to implement a security model, not a obfuscation model.

Having said that, there's no reason the author can't use both tools. Plus it would save the two costly listLast function calls.

for instance:

Isapi re-write rule:
RewriteRule ^/images/(.*) /images/?image_path=$1 [NC,L,P]

Then in the image.cfm
Add your code, but make the URL.image_path EQ plus whatever security checks that are needed.

@Jim,

I didn't mean to shoot your idea down - one of the option's suggested was pure obfuscation, which the ISAPI Rewrite could definitely help with.

This is actually a pretty important topic for quite a few people. The issue comes also when concerned about resource leaching. If you have a Linux machine you can solve a lot of the problem by an .htaccess on your images directory but if not you want to be able to protect other resources, perhaps not even make them web-accessible except to valid or logged on users.

In order to restrict access to only those people with current sessions you need to have some means of delivering (streaming) the content after determining whether a valid session exists.

How, if not using cfcontent or the java equivalent, do you protect the resources AND hide from the user its true location?

This is an interesting post, Ben how would your solution measure in terms of speed/caching?

If I request this
/images/image01.jpg
it will load fully the first time but the second time it may go directly to local cache depending on browser settings, improving performance.

Wouldn't this
/images/?image01.jpg

load an image every single time?

@Fernando,

I believe that caching works on the entire URL, not just the script name. As long as the query string doesn't change, I think the caching will know how to include it.