DEV Community

James Moberg
James Moberg

Posted on

3

ColdFusion SetCookie UDF (Supports “SameSite”)

I attempted to post the following response regarding a ColdFusion bug that I reported back in March 2018, but Adobe's CFTracker web application wasn't working and refused to accept my post for an undisclosed reason.

CFCookie "samesite" support
https://tracker.adobe.com/#/view/CF-4201688

A third-party site's API recently triggered the following warning message in Chrome 78:

A cookie associated with a cross-site resource at http://jetio.streamguys.com/ was set without the SameSite attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with SameSite=None and Secure. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032

I reported it to them and they fixed it within 2-3 days on their PHP platform.

As a workaround for CF2016 (and CF10 & 11), I'm using this modified UDF to set a CFCookie & a fallback "set-cookie" CFHeader. It's duplicates the response headers, but if CFCookie isn't used, the value isn't added to the COOKIE scope. (The 2nd set-cookie header automatically overwrites the first one set by CF.)

Source Code

<cfscript>
/* 9/15/2010 http://www.modernsignal.com/coldfusionhttponlycookie
<cfset SetCookie(name="logintoken", value="sometoken", secure=true, httponly=true)>
11/1/2019 Updated to add preserveCase & samesite support
Replacement for cfcookie that handles httponly & samesite cookies
3/12/2024 Support for CHIPS Partitioned https://developers.google.com/privacy-sandbox/3pcd/chips
https://community.adobe.com/t5/coldfusion-discussions/setting-partition-with-cfcookie/m-p/14484584#M197250 */
void function setCookie(required string name, required string value, any expires="", boolean secure=false, string path="/", string domain="", boolean httpOnly=false, boolean encodeValue=true, boolean preserveCase=false, string samesite="", boolean partitioned=false) output=false hint="Replacement for cfcookie that handles httponly & samesite cookies" {
// None, Strict, Lax
if (request.isFlushed()){
return;
}
local.cookieData = [];
local.expDate = "";
arguments.name = (arguments.preserveCase) ? arguments.name : ucase(arguments.name);
if (arguments.partitioned){
arguments.secure = true;
arguments.samesite = "none";
arguments.httpOnly = false;
arguments.path = "/";
arguments.name = "__Host-#arguments.name#";
arguments.preserveCase = true;
}
if (arguments.encodeValue){
arrayappend(local.cookieData, "#arguments.name#=#urlencodedformat(arguments.value)#");
} else {
arrayappend(local.cookieData, "#arguments.name#=""#replace(argumments.value,'"','\"')#""");
arrayappend(local.cookieData, "Version=1");
}
switch (arguments.expires){
case "":
break;
case "now":
local.expDate = dateadd("d", -1, now());
break;
case "never":
local.expDate = dateadd("yyyy", 30, now());
break;
case "session":
local.expDate = "";
break;
default:
if (isvalid("date", arguments.expires)){
local.expDate = arguments.expires;
} else if (isnumeric(arguments.expires)){
local.expDate = dateadd("d", arguments.expires, now());
}
break;
}
if (isvalid("date", local.expDate)){
local.expDate = dateconvert("local2Utc", local.expDate);
arrayappend(local.cookieData, "Expires=#datetimeformat(local.expDate, 'ddd, dd mmm yyyy HH:nn:ss')# -0000");
}
if (len(arguments.domain)){
arrayappend(local.cookieData, "Domain=#arguments.domain#");
}
if (len(arguments.path)){
arrayappend(local.cookieData, "Path=#arguments.path#");
}
if (arguments.httponly){
arrayappend(local.cookieData, "HttpOnly");
}
if (listfindnocase("none,strict,lax", trim(arguments.samesite))){
arrayappend(local.cookieData, "SameSite=#trim(arguments.samesite)#");
}
if (arguments.partitioned){
arrayappend(local.cookieData, "Partitioned");
}
if (!len(arguments.expires)){
structdelete(arguments, "Expires");
}
if (arguments.secure || listfindnocase("none", trim(arguments.samesite))){
arrayappend(local.cookieData, "Secure");
}
// generate cfcookie (using params that it allows); Don't use cfcookie to create CHIPS partitioned cookies.
if (!arguments.partitioned){
structdelete(arguments, "partitioned");
cfcookie( attributecollection=arguments );
}
// generate cookie header (this will overwrite cfcookie)
cfheader( name="Set-Cookie", value=trim(arraytolist(local.cookieData, '; ')) & ";");
}
</cfscript>
view raw SetCookie.cfm hosted with ❤ by GitHub

1/17/2020 Update

Adobe has indicated that this will be fixed in CF2016+, but it's 20 days away and nothing has been made available yes. On 1/16/2020, Google published Get Ready for New SameSite=None; Secure Cookie and listed other platforms that had same-site examples.


3/24/2020 Update

Adobe has posted manual patches for CF2016 & CF2018 on the bug report. (If using CF2016, download the CF2018 attachment as the instructions for CF2016 are incorrect.)

If you are still using ColdFusion 10 or 11, you can use this UDF or Pete Freitag's solution for IIS or Apache.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (1)

Collapse
 
jdsplicer profile image
JD • Edited

James, for CF-4201688 did you actually get it to work? I am running CF2018 Enterprise ver. 2018.0.08.318307 on Websphere 9 and OS: RedHat Enterprise Linux 7.2. We applied hf201600-4201688 as the instructions stated; however, I noticed "Catalina.jar" file does not exists in any directory as I believe that is used on Tomcat. It doesn't appear that the samesite attribute of cfcookie works. I don't receive an error for that samesite attribute like I did before applying the fix but it doesn't appear to pass the value.

Example: cfcookie name="TEST" value="abc123" samesite="None"
Cookie Result:
NAME: "AMWEBJCT!%2Fjrtlappsdev!TEST"
VALUE: "abc123"

SAMESITE: is empty

Thanks.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 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