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 CFinNC 2009 (Raleigh, North Carolina) with:

Exploring ColdFusion Component Runtime Class Properties And Serialization

By Ben Nadel on
Tags: ColdFusion

Yesterday, I came across an interesting post by John Whish on getting an entity memento in ColdFusion. As suggested by Sam Farmer, John was using SerializeJSON() and DeserializeJSON() to serialize a ColdFusion component into a JSON string and then reserializing it back into a plain old ColdFusion struct. Typically, serialization wouldn't seem unusual; but, what struck me as very odd about John's findings was that the SerializeJSON() method was actually able to grab the private properties of the component instance.

I don't know what component serialization is typically used for; but, the fact that it can access private properties just seemed confusing. Now, I don't really care that much about in-house security, but being able to access private properties via serialization seems like a some sort of a security flaw? But again, I really don't care much about the security implications of this - what I do care about is simply understanding how it works. As such, I felt this required a little bit more exploration.

 
 
 
 
 
 
 
 
 
 

How are these private proprieties of the component being access? Is ColdFusion using the implicit getters defined by the CFProperty tags? Is it simply reaching into the variables scope? Or, is it using some combination of these two approaches?

To test this, I defined a Girl.cfc ColdFusion component with three properties: name, hair, and bust. Of these properties, only the "name" property has a CFProperty tag (which defines an implicit getter). The "hair" property has no CFProperty tag; but, it does have an explicitly defined getHair() accessor method which always returns "Blonde," regardless of what variables.hair actually contains. The "bust" property has neither a CFProperty tag nor an explicitly defined accessor method. With this combination of conditions across the properties, it should shed some light on how the serialization is accessing the component's private properties.

Girl.cfc

  • <!---
  • NOTE: We are defining the accessors attributes. As such,
  • implicit getters / setters *can* be defined for each of
  • the CFProperty tags.
  • --->
  • <cfcomponent
  • output="false"
  • accessors="true"
  • hint="I am a component with some private variables.">
  •  
  •  
  • <!---
  • We are only defining a single property; and that property
  • also has a GETTER defined for it. This will create an
  • actual getName() method in the component.
  • --->
  • <cfproperty name="name" type="string" getter="true" setter="false" />
  •  
  •  
  • <cffunction
  • name="init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I return an initialized component.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="name"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the name property."
  • />
  •  
  • <cfargument
  • name="hair"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the hair property."
  • />
  •  
  • <cfargument
  • name="bust"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the bust property."
  • />
  •  
  • <!--- Set the default propreties. --->
  • <cfset variables.name = arguments.name />
  • <cfset variables.hair = arguments.hair />
  • <cfset variables.bust = arguments.bust />
  •  
  • <!---
  • Return this component reference. Remember, when
  • using the new() method, we must explicitly return
  • an object reference.
  • --->
  • <cfreturn this />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="getHair"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="I return a FALSE hair type.">
  •  
  • <!--- Return blonde no matter what. --->
  • <cfreturn "Blonde" />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="upgradeProperties"
  • access="public"
  • returntype="any"
  • output="true"
  • hint="I upgrade the component meta data that is involved in the serialization of the component properties.">
  •  
  • <!---
  • Create an array to hold our property attributes as
  • they are defined in the component meta data.
  • --->
  • <cfset local.attributes = [] />
  •  
  • <!--- Loop over the property names. --->
  • <cfloop
  • index="local.property"
  • array="#[ 'name', 'hair', 'bust' ]#">
  •  
  • <!---
  • Create the attribute collection for this property
  • (NOTE: We have to use attribute collection - struct
  • won't work).
  • --->
  • <cfset local.attribute = createObject(
  • "java",
  • "coldfusion.runtime.AttributeCollection"
  • ).init()
  • />
  •  
  • <!--- Set attribute values. --->
  • <cfset local.attribute[ "name" ] = local.property />
  • <cfset local.attribute[ "getter" ] = "true" />
  •  
  • <!---
  • Add this attribute to the property attribute
  • collection array.
  • --->
  • <cfset arrayAppend(
  • local.attributes,
  • local.attribute
  • ) />
  •  
  • </cfloop>
  •  
  • <!--- Overwrite the meta data properties. --->
  • <cfset getMetaData( this ).properties = javaCast(
  • "java.lang.Object[]",
  • local.attributes
  • ) />
  •  
  • <!--- Return the updated meta data. --->
  • <cfreturn getMetaData( this ) />
  • </cffunction>
  •  
  • </cfcomponent>

In addition to the various properties, the Girl.cfc ColdFusion component also has a method called upgradeProperties(). This method alters the runtime meta data of the component definition to define all three properties as having a "getter". With this component in place, I then created a test script that tried to serialize the a Girl.cfc instance both before and after the upgradeProperties() method was invoked:

  • <!--- Create a girl object. --->
  • <cfset girl = new Girl(
  • name = "Joanna",
  • hair = "Brunette",
  • bust = "34A"
  • ) />
  •  
  •  
  • <cfoutput>
  •  
  • <!--- Get the serialized data for this component. --->
  • JSON: #serializeJSON( girl )#<br />
  • <br />
  •  
  •  
  • <!---
  • Now, let's upgrade the runtime propreties of the
  • component class definition to include GETTERs.
  •  
  • NOTE: This does not actually define the getters,
  • it only defines the properties.
  • --->
  • <cfset girl.upgradeProperties() />
  •  
  •  
  • <!---
  • Now that we have upgraded the meta data definiton,
  • let's try to serialize the component again.
  • --->
  • JSON: #serializeJSON( girl )#<br />
  • <br />
  •  
  •  
  • <!---
  • Try to get one of the properties as if there were
  • implicit getters.
  • --->
  • <cftry>
  • Bust: #girl.getBust()#<br />
  • <br />
  •  
  • <cfcatch>
  •  
  • getBust() not defined.<br />
  • <br />
  •  
  • </cfcatch>
  • </cftry>
  •  
  • </cfoutput>
  •  
  •  
  • <!---
  • Now that we have updated our runtime properties, let's
  • output the component meta data.
  • --->
  • <cfdump
  • var="#getMetaData( girl )#"
  • label="Runtime Meta Data"
  • />

As you can see, we're getting the JSON value of the Girl.cfc instance before and after the runtime class definition is altered. We also try to access any newly created implicit getter for the "bust" method. When we run the above code, we get the following page output:

 
 
 
 
 
 
ColdFusion Component Serialization (JSON) Before And After Altering The Runtime Component Definiton. 
 
 
 

What we have here is very interesting! When we first try to serialize the Girl.cfc instance, the only property ColdFusion accessed was "name" - the one defined explicitly by a CFProperty tag. Of course, this doesn't tell us if the property was access via an implicit getter or directly from the variables scope:

JSON: {"name":"Joanna"}

After we call the serialize the first time, we then alter the runtime properties of the component to set all three properties as being "getter"'able. Once we do that, the situation changes drastically. Now, all three properties are access during serialization:

JSON: {"bust":"34A","name":"Joanna","hair":"Blonde"}

But, let's not jump to conclusions about how these properties are being accessed. As demonstrated by the "Blonde" hair property, ColdFusion is clearly making use of our explicitly defined "getHair()" method which ignores any existing hair value. But what about the "bust" property? We have no explicitly defined getter, so ColdFusion has either created a just-in-time accessor method; or, it has circumvented the component's private scope security.

To test "bust" access, I tried to make use of any newly defined getBust() method. Unfortunately, this results in an exception, which I handle in a CFTry / CFCatch block:

Bust: getBust() not defined.

A quick CFDump of the newly upgraded component meta data confirms that while the properties have been updated, no new getter methods have been defined. This begs the question: are implicit getter methods actually created for CFProperty tags? Or, are implicit getter methods handled in a more "onMissingMethod" type fashion? Well, if you look at the one CFProperty tag that we did define in our component - the name property - you'll see that the getName() accessor is listed as one of the physically defined class methods. As such, I think it's fair to say that ColdFusion handles implicit accessors with physically defined methods.

From this experimentation, I think we can draw the conclusion that component serialization behavior is dictated primarily through the class properties. If a property is defined as having a "getter", that property will be returned in serialization one way or another. If either an implicit or explicit getter method is available, ColdFusion will use that method for data access. If, however, neither of those methods are available, ColdFusion will simply bypass scope security and access the given property directly in the Variables scope.

It should be noted that if you have Template Caching turned on, once you update the properties defined in the component meta data, it will be implicitly updated for any components subsequently instantiated. In fact, if I were to refresh the page used in the above demo, both calls to SerializeJSON() would be the same. For performance reason, the component meta data is only compiled when a particular component class is first instantiated; after that, all new instances of the given component use the pre-compiled meta data.




Reader Comments

Hi Ben, nice to have you back! I already gave up on you, thinking you'll write about jQuery and iPhone for the rest our our lives! :)

Reply to this Comment

Saving private properties is necessary so that you can "reconstitute" an object on the other side of the wire, or load up a serialized object you saved to disk.

If it didn't save the private state of the object you'd lose that information and possibly end up with an object in an unusable state on the other side.

This is a pretty common way to deep copy objects in other languages too. For instance in ruby you do:

# CF: clone = DeserializeJSON(SerializeJSON(object))
clone = Marshal.load(Marshal.dump(object))

Reply to this Comment

@Ben Very useful analyses. Thank you
@Elliot Thanks for additional clarification

Though, it's quite a shame that getBust() failed...not defined ;)

Reply to this Comment

@Elliott,

I guess I've never done Enterprise type architectures where I'm actually passing around full-on objects. I figured I would typically communicate through an API. I can't even think of a system where I would pass around objects.

@Marko,

In my experience, getBust() is *always* shot down :)

Reply to this Comment

@Elliott, according to Ben's experiment, serializeJSON() doesn't access the private data by default - it doesn't even access the getHair() method - so trying to clone a Girl.cfc via serializeJSON/deserializeJSON wouldn't actually work.

@Ben, very interesting approach to modify the component metadata to show that serializeJSON() works from that rather than anything else.

Note that you can easily inject a method into a CFC that returns the entire variables scope so security is a bit squishy in such a dynamic language (and that would then allow you to figure out what needed added to the metadata in order to modify what serializeJSON() did).

Reply to this Comment

@Sean,

After doing this, I wanted to play with the functions listed in the meta data. I thought maybe you could use the meta data to create a sort of run-time prototypal inheritance or fly-weight pattern... but no. If you add a function to the meta data, it'll be sticky to the meta-data, but won't actually be available in the component.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.