For months, I've been trying to track down what I thought was a Cookie problem at InVision App. Every now and then, we'd start logging thousands upon thousands of cookie errors. Finally, I realized that it wasn't a problem with the cookie itself, per-say, but rather with the fact that the ColdFusion CFParam tag doesn't really care about white-space.
To demonstrate this, we can execute a CFParam tag with embedded white-space and then look at how the data structures are mutated:
<cfscript> // Creating a white-space variable for more obvious injection. __ = repeatString( " ", 10 ); // Param a struct-key with embedded white-space. param name="#__#url#__#.#__#foo#__#" type="string" default="bar"; // Output the value of the non-white-space version. writeOutput( "foo: " & url.foo ); </cfscript>
Here, you can see that I am including 10 spaces before and after each token in the CFParam expression. However, after the param tag has been executed, I'm looking up the value using trimmed references:
As you can see, the white-space embedded in the Name expression had no bearing on the values involved. This is the same way that white-space has very little bearing on how the rest of your code is compiled and executed. This is correct. This is proper tokenization of the code.
But, it did trip us up when it came to setting cookies. Somewhere in the codebase, we had a little piece of control flow that was paraming a cookie value based on another variable value. We were using the CFParam tag to setup the default and then subsequently referencing the cookie key later on in the same pathway. It looked something like this:
<cfscript> // Creating a white-space variable for more obvious injection. __ = repeatString( " ", 10 ); // Define a cookie name with TRAILING white-space. cookieName = "foo#__#"; // Param the cookie value. param name="cookie.#cookieName#" type="string" default="bar"; // Check to see if the param'd cookie value exists. writeOutput( "Cookie exists: " & structKeyExists( cookie, cookieName ) ); // Check for a version with no white-space. writeOutput( "<br />" ); writeOutput( "foo exists: " & structKeyExists( cookie, trim( cookieName ) ) ); </cfscript>
As you can see, the cookie "name" in this case has trailing white-space. Now, cookie names can't have white-space (or commas or semi-colons or non-ASCII values). So, you might have expected this code to throw an error. But, since we were using the CFParam tag to setup the default value, the trailing white-space, on the key-name, was essentially being ignored during execution. As such, the value was stored in a cookie key that was being implicitly trimmed.
However, since the cookie name contained trailing white-space according to the variable, cookieName, subsequent references to the cookie scope would throw the following ColdFusion error:
Element XYZ is undefined in a Java object of type class coldfusion.runtime.CookieScope
This is why the first structKeyExists() fails (uses key with trailing spaces) and the second structKeyExists() succeeds (uses trimmed key):
Cookie exists: NO
foo exists: YES
Nothing here is buggy except for, perhaps, the approach we were using. But, you can certainly imagine that a trailing space was next to impossible to even see in the logging output. Oh well, you live and you learn - at least I finally figured out what the heck was going wrong. That's thousands of errors that will stop showing up in Kibana!
Want to use code from this post? Check out the license.