DEV Community

Cover image for TempCache ColdFusion UDF
James Moberg
James Moberg

Posted on

1

TempCache ColdFusion UDF

There's been many occasions where a user-specific payload has been generated (shopping cart, check out, config settings, processing results) and the user needs to be directed to a new destination with the data, but I want to avoid non-securely passing data as URL or form parameters or having to enable and/or leverage session variables.

We've encountered issues where the content could be blocked due to complex WAF rules that are beyond our editable control... especially if there's anything that resembles HTML or contains certain sequences. There's also abuse issues as automated software can scan the form, fuzz the parameters in order to blindly auto-post to the final script. We experienced this in the form of carding (aka credit card stuffing) on some non-profit donation forms.

To prevent abuse, we've added hCaptcha, CSRF, fingerprinting, IP reputation and some other countermeasures, but there's been some sophisticated abuses where everything (remote IP, form payload, browser) is completely different, but it's obvious that it's the same abuser due to the automated schedule and occurrence of failures.

The most impactful workflow has been to:

  • display a verification page
  • Create an object with data unique to the order (IP, email, total amount) , temporarily cache it server-side and generate a token to add to the form
  • Upon submission, and prior to performing any transaction, use the UUID to perform a look-up of cached data. If the look-up data doesn't exist or doesn't match the form/CGI data, reject the attempt.
  • Added bonus: If no cached data exists, sleep for a second or two and then return a bogus "credit card is invalid" message.

We've also used this script on Contact & "Thank you" pages. On some older applications, the response is displayed on the same page without redirecting or using history.pushState(null, null, "/myUrl"); to prevent accidental POST resubmission, but some app-based browsers seem to be blindly retriggering the form post when reopening the app. We haven't been able to determine the actual cause, but capturing the response message, adding it to an object, caching and redirecting to a new page with the UUID to display the content has prevented the form report/replay issues from reoccurring.

Here's the TempCache UDF. A simple cfml example has been included:

https://gist.github.com/JamoCA/fd43c189379196b6a52884affea3ad51

Source code

<!--- tempCache UDF (2019-11-22) By SunStar Media
ColdFusion UDF to temporarily cache data and return a UUID. Used for verifying form posts (ie, like CSRF) or building magic
link passwordless logins for monolith web application.
GIST: https://gist.github.com/JamoCA/fd43c189379196b6a52884affea3ad51
Twitter/X: https://x.com/gamesover/status/1803866104491839620
Blog: https://dev.to/gamesover/tempcache-coldfusion-udf-32f9
--->
<cfscript>
public any function tempCache(
any inputObject,
numeric minutes=5,
numeric maxMinutes=5,
boolean singleUse=false,
string cachePrefix="tempCache_"
) hint="Temporarily cache data for x minutes. (Pass object; returns UUID. Pass UUID, returns object.)" {
local.response = "";
local.minutes = (val(arguments.minutes) gt 0) ? val(arguments.minutes) : 5;
local.maxMinutes = abs(val(arguments.maxMinutes)) + local.minutes;
if (issimplevalue(arguments.inputObject) && isvalid("UUID", arguments.inputObject)){
local.response = cacheget("#arguments.cachePrefix##arguments.inputObject#");
if (isnull(local.response)){
local.response = {};
} else if (arguments.singleUse){
cacheremove("#arguments.cachePrefix##arguments.inputObject#");
}
} else {
local.response = createuuid();
cacheput("#arguments.cachePrefix##local.response#", arguments.inputObject, createtimespan(0, 0, local.maxMinutes, 0), createtimespan(0, 0, local.minutes, 0));
}
return local.response;
}
</cfscript>
<cfparam name="url.Token" default="">
<cfset tempConfig = [
"inputObject": [
"html": "<p>Hello World #datetimeformat(now(), "iso")#</p>",
"IPAddress": CGI.REMOTE_ADDR,
"timestamp": datetimeformat(now(), "iso")
],
"minutes": 5,
"maxMinutes": 5,
"singleUse": false
]>
<h2>tempCache Demo</h2>
<cfif len(url.Token)>
<fieldset>
<cfoutput>
<legend>Fetching "#url.Token#" from tempCache UDF</legend>
</cfoutput>
<cfif isvalid("UUID", url.Token)>
<cfset cacheResults = tempCache(url.Token)>
<cfif !structcount(cacheResults)>
<p style="color:red;"><b>No results.</b> Cache key doesn't exist</p>
<cfelseif !cacheResults.keyexists("IPAddress")>
<p style="color:red;"><b>Suspicious:</b> Cache key exists, but not IP address.</p>
<cfelseif cacheResults.IPAddress neq CGI.REMOTE_ADDR>
<p style="color:red;"><b>Suspicious:</b> IP address exists, but not same as current request.</p>
<cfelse>
<p style="color:green;"><b>Good:</b> IP address exists and is the same as current request.</p>
</cfif>
<cfoutput>
<p><b>Retry Token:</b> <a href="?Token=#url.Token#">#url.Token#</a></p>
</cfoutput>
<cfdump var="#cacheResults#" label="Cached data for #url.Token#">
<cfelseif len(url.Token)>
<p style="color:green;"><b>Invalid:</b> ID is not a UUID</p>
</cfif>
</fieldset>
</cfif>
<cfset newToken = tempCache(argumentcollection=tempConfig)>
<cfoutput>
<fieldset>
<legend>New Token:</legend>
<p><a href="?Token=#newToken#">#newToken#</a></p>
<cfdump var="#tempConfig#" label="New ID Config (debugging)">
</fieldset>
</cfoutput>
view raw TempCache.cfm hosted with ❤ by GitHub

Image of AssemblyAI

Automatic Speech Recognition with AssemblyAI

Experience near-human accuracy, low-latency performance, and advanced Speech AI capabilities with AssemblyAI's Speech-to-Text API. Sign up today and get $50 in API credit. No credit card required.

Try the API

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay