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.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

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.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay