Coldfusion Log Files and Timezone/Locales - datetime

We have an application that records information into a database and if there is an erorr, we are logging it in a log file. We have users spread across the globe. If something does happen, we'd like to record the timestamp of the issue but we need the timestamp to be uniformed meaning someone in Hong Kong vs California, should have the same timestamp with appropriate timezone offset. I dont know how to do this in CF.
Dealing with dates has always been a week skill set of mine and would appreciate some help figuring this one out.
Here is the code which writes out the log file
<cftry>
... insert into db here ...
<cfcatch type="any">
<cfset error_msg = '#createodbcdatetime(now())#|#cfcatch.Message#|#cfcatch.Detail#|#cgi.HTTP_REFERER#|#cgi.SERVER_NAME#|#cgi.SCRIPT_NAME#|#cgi.QUERY_STRING#'>
<cftry>
<cfif FileExists(ExpandPath(#log_name#))>
<cflock name="WebSiteErrorLog_Lock" type="exclusive" timeout="30">
<cffile action="append" addnewline="yes" file="#currentDirectory##log_name#" mode="777" output="#error_msg#">
</cflock>
<cfelse>
<cflock name="WebSiteErrorLog_Lock" type="exclusive" timeout="30">
<cffile action="write" addnewline="yes" file="#currentDirectory##log_name#" mode="777" output="#error_msg#">
</cflock>
</cfif>
<cfcatch type="any"></cfcatch>
</cftry>
</cfcatch>
but this line is probablly all that is really needed for this SO question:
<cfset error_msg = '#createodbcdatetime(now())#|#cfcatch.Message#|#cfcatch.Detail#|#cgi.HTTP_REFERER#|#cgi.SER
TIA

I am curious, are you using ColdFusion 9? Because you may want to look into implementing the OnError function in your Application.cfc. It may be a bit more convenient than this constant Try-Catch approach.
That being said, the following function will return formatted UTC time. I think I stole it from someone else's answer previously on this site.
<cffunction name="getUTCTime" access="public">
<cfscript>
var serverTime=now();
var utcTime=GetTimeZoneInfo();
var utcStruct=structNew();
utcStruct.Hour=DatePart("h", serverTime);
utcStruct.Minute=DatePart("n", serverTime);
utcStruct.Hour=utcStruct.Hour + utcTime.utcHourOffSet;
utcStruct.Minute=utcStruct.Minute + utcTime.utcMinuteOffSet;
if (utcStruct.Minute LT 10) utcStruct.Minute = "0" & utcStruct.Minute;
</cfscript>
<cfreturn utcStruct.Hour & ":" & utcStruct.Minute>
</cffunction>
Update:
As mentioned in the comments, the above code is unnecessary and buggy. Its preferable to use the built-in dateConvert() function (available since at least MX7):
<cfset timeString = timeFormat( dateConvert("local2utc", now()), "HH:mm")>

Related

Changing the Session Languge leads to "java.text.ParseException: Unparseable date

whenever I'm defining the timeframe being in German session language after changing to English lang. session (and vice versa) I'm getting the:
java.text.ParseException: Unparseable date: "10.10.2018"
Here is the fragment:
Date startDateFormatted = DateUtils.convertDateToMinusDayNumber(cal, dayRange);
Date endDateFormatted = new Date();
if (StringUtils.isNotEmpty(startDate) && StringUtils.isNotEmpty(endDate))
{
try
{
String datePattern = getLocalizedString("dd.MM.yyyy"); //
startDateFormatted = new SimpleDateFormat(datePattern).parse(startDate); // exception is throwing on this line
endDateFormatted = new SimpleDateFormat(datePattern).parse(endDate);
}
catch (final Exception e)
{
LOG.error(ERROR_DATE_PARSING, e);
}
}
java.time
I recommend you use java.time, the modern Java date and time API, for your date work.
String datePattern = "dd.MM.uuuu";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(datePattern);
String startDateString = "10.10.2018";
LocalDate startDate = LocalDate.parse(startDateString, dateFormatter);
System.out.println(startDate);
Output:
2018-10-10
If you want to support different date formats for different locales, let Java handle that part for you:
String datePattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.GERMAN);
German locale works with your example string of 10.10.2018. For UK locale, for example, a string like 10 Oct 2018 would be required instead, as Britons would typically expect.
What went wrong in your code?
We cannot tell from the information and code that you have provided exactly what happened. A couple of good guesses are:
As Arvind Kumar Avinash said in a comment, getLocalizedString() may be causing trouble. You may print datePattern to check. Localization is something you do to strings that you display to the user. Trying to localize a format pattern string for a formatter is probably plain wrong, so you should leave out that method call. That the error occurs when changing language seems to support this possibility.
There may be unexpected non-printing characters in your string. One way to check would be to print startDate.length(). If the length is greater than 10, there are more characters than the 10 chars in 10.10.2018.
Link
Oracle tutorial: Date Time explaining how to use java.time.

ColdFusion decrypt encodings are not the same

I am trying to decrypt a string using ColdFusion's Decrypt() function, but am getting an "encodings are not the same..." error.
These are the steps I was instructed to take from the string's source:
Create an MD5 hash of the shared key
Using TripeDES with the cipher of ECB, decrypt the encrypted string with the MD5 hash of the shared key
Code:
<cfset qKey = hash('shared_key','MD5') />
<cfset dc = Decrypt('string_to_decrypt', qkey, 'DESEDE/ECB/PKCS5Padding', 'Base64') />
<cfdump var="#dc#">
Error:
An error occurred while trying to encrypt or decrypt your input string: The input and output encodings are not same.
Update:
Also tried the following, but it throws the same error:
<cfset finalText = "WVJrOdkntkQ%3d">
<cfset theKey = "S3C016" />
<cfset theKeyInBase64 = toBase64(theKey)>
<cfset hashedKey = hash( theKeyInBase64, "md5" ) />
<cfset padBytes = left( hashedKey, 16 ) />
<cfset keyBytes = binaryDecode( hashedKey & padBytes , "hex" ) />
<cfset finalKey = binaryEncode( keyBytes, "base64" ) />
<cfset decrypted = decrypt( finalText, finalKey, "DESede/ECB/PKCS5Padding", "base64" ) />
Decrypted String: <cfdump var="#decrypted#">
These are the steps I was instructed to take
Honestly, I always find it a little disturbing when I come across a recommendation for using TripleDES these days. As Zach pointed out it is no longer considered secure and was replaced by stronger algorithms like AES. Also, you can clearly see from wiki article images an illustration of why ECB mode is discouraged: "it does not hide data patterns well". (Side note, in the area of hashing MD5 was also surpassed by stronger algorithms a while ago). Bottom line, I strongly suggest discussing the chosen algorithms with whomever gave you the instructions, because they are not very secure.
However, to answer your question, there are a couple of issues:
Your code says the encrypted value is base64 encoded. However, it includes a percent % symbol which is not a valid character for base64. I am guessing that value was url encoded and must be decoded first:
<cfset encryptedText = urlDecode("WVJrOdkntkQ%3d")>
Hash() returns a hexadecimal string. The encrypt() function requires that keys be base64 encoded.
An MD5 hash returns sixteen (16) bytes. While that is a valid key size for TripleDES, the CF/Java implementation only supports 24 byte keys. You need to pad the key with the first eight (8) bytes to make it the proper size.
<cfset originalKey = hash("S3C016","MD5")>
<cfset padChars = left(originalKey, 16)>
<cfset keyBytes = binaryDecode(originalKey & padChars, "hex")>
<cfset newKeyString = binaryEncode(keyBytes, "base64")>
Making the changes above will resolve the error and allow you to decrypt the string successfully. The result should be "20".
<cfset dc = Decrypt(encryptedText, newKeyString, "DESEDE/ECB/PKCS5Padding", "Base64") />
<cfdump var="#dc#">
3DES uses a 24-byte key, MD5 produces a 16-bytes. Many times the first 8-bytes are also used for the final 8-bytes. Some implementations do tis automatically, others zero pad, others---who knows.
Try creating a 24-byte key by duplication the first 8-bytes to the 16-bytes produced by MD5.
Encoding the 16-bytes produced by MD5 really does not make sense in a cryptographic sense eventhough it will prodice 24-bytes.

Peculiar error with ColdFusion on BlueDragon.NET

We've got an odd issue occurring with ColdFusion on BlueDragon.NET. Asking here because of the broad experience of StackOverflow users.
Tags inside POSTed content to out BlueDragon.NET server gets removed, and we're not sure where in the stack it's getting removed. So for example if we post this data
[CORE]
Lesson_Status=Incomplete
Lesson_Location=comm_13_a02_bs_enus_t17s06v01
score=
time=00:00:56
[Core_Lesson]
<sd ac="" pc="7.0" at="1289834380459" ct="" ><t id="lo8" sc=";;" st="c" /></sd>
<sd ac='' pc='7.0' at='1289834380459' ct='' ><t id='lo8' sc=';;' st='c' /></sd>
<sd ac="" pc="7.0" at="1289834380459" ct="" ><t id="lo8" sc=";;" st="c" /></sd>
<sd ac="" pc="7.0" at="1289834380459" ct="" ><t id="lo8" sc=";;" st="c" /></sd>
<b>hello1</b>
<i>hello2</i>
<table border><td>hello3</td></table>
<sd>hello4</sd>
<sd ac="1">hello5</sd>
<t>hello6</t>
<t />
<t attr="hello8" />
<strong>hello10</strong>
<img>
><>
What we get back is this:
[CORE]
Lesson_Status=Incomplete
Lesson_Location=comm_13_a02_bs_enus_t17s06v01
score=
time=00:00:56
[Core_Lesson]
hello1
hello2
hello3
hello4
hello5
hello6
hello10
>
That is, anything that starts with < and ends with > is getting stripped or filtered and no longer appears in ColdFusion's FORM scope when it's posted.
Our server with BlueDragon JX does not suffer this problem.
If we bypass using the default FORM scope and use this code, the tag-like content appears:
<cfscript>
// get the content string of the raw HTTP headers, will include all POST content as a long querystring
RAWREQUEST = GetHttpRequestData();
// split the string on "&" character, each variable should now be separate
// note that at this point duplicate variables will get clobbered
RAWFORMFIELDS = ListToArray(RAWREQUEST.content, "&");
// We're creating a structure like "FORM", but better
BetterFORM = StructNew();
// Go over each of the raw form fields, take the key
// and add it as a key, and decode the value into the value field
// and trap the whole thing if for some reason garbage gets in there
for(i=1;i LTE ArrayLen(RAWFORMFIELDS);i = i + 1) {
temp = ListToArray(RAWFORMFIELDS[i], "=");
try {
tempkey = temp[1];
tempval = URLDecode(temp[2]);
StructInsert(BetterFORM, tempkey, tempval);
} catch(Any e) {
tempThisError = "Malformed Data: " & RAWFORMFIELDS[i];
// Log the value of tempThisError here?
// WriteOutput(tempThisError);
}
}
</cfscript>
<cfdump var="#BetterFORM#">
If we do this, and use the created BetterFORM variable, it's there, so it does not seem to be a problem with the requests being filtered at some other point in the stack. I was thinking maybe it was URLScan, but that appears not to be installed. Since BD.NET runs on .NET as the engine, perhaps there's some sanitization setting that is being used on all variables somehow?
Suggestions, ideas, etc are welcome on this issue.
I don't have a BD.NET instance handy to check, but Adobe ColdFusion has a setting in the cf administrator to strip "invalid tags". That's my best guess. Adobe CF replaces them with "invalidTag", my guess is that BD.Net just strips it silently.
It turned out to be very mundane.
We had a custom tag that did customized string replacements. On one server, it was modified to NOT replace all tags. On this server, we were using an older version that did. So the fault was not a difference between BlueDragon JX and BlueDragon.NET -- it was developer team error.

Error with Decrypt for "Could not perform unpadding: invalid pad byte.."

Using CF8 and MySQL 5.1, I am trying to encrypt() a password upon creation and then decrypt() at login. I can get the decrypt() to work fine on a test page but when I put it in a cfincluded page with cflogin I get the error "An error occurred while trying to encrypt or decrypt your input string: com.rsa.jsafe.crypto.dr: Could not perform unpadding: invalid pad byte.. ". It is the same code and DB from my test page to my app.
application.cfc:
<cfif NOT IsDefined("Request.PasswordKey")>
<cfset request.PasswordKey = generateSecretKey("AES")>
<cfset request.algorithm = "AES">
<cfset request.encoding = "hex">
</cfif>
test page which works fine:
FORM DATA: <br/>
form password:<cfoutput>#form.passwd#</cfoutput><br/>
<cfset encrypted = Encrypt(form.passwd,Request.PasswordKey,Request.algorithm,Request.encoding)>
Encrypted: <cfoutput>#encrypted#</cfoutput><br/>
Decrypted: <cfoutput>#Decrypt(variables.encrypted,Request.PasswordKey,Request.algorithm,Request.encoding)#</cfoutput><br/>
<br/>
QUERY DATA<br/>
<cfinvoke component="components.userQ" method="login" returnvariable="qLogin">
<cfinvokeargument name="formData" value="#form#">
</cfinvoke>
<cfoutput>qLogin password: #qlogin.encPasswd#</cfoutput><br/>
<cfoutput>Decrypted encPasswd from qLogin: #Decrypt(qlogin.encPasswd,Request.PasswordKey,Request.algorithm,Request.encoding)#</cfoutput>
Decrypt() in app page that is erroring:
<cfset unEnPasswd = #Decrypt(qlogin.encPasswd,Request.PasswordKey,Request.algorithm,Request.encoding)#>
I can get the default CFMX_COMPAT encrypt() and decrypt() to work fine in my app with the same code, just changing the key, algorithm, and encoding variables.
BTW, I am also storing the encrypted strings as varchar() in the DB so it doesn't mess up the padding (so I read). I tried BLOB but get a bytearray error.
Any help or thoughts are greatly appreciated.
You're creating a new secret key on every request,
Really your code should be more like:
<cffunction name="onApplicationStart" returnType="boolean" output="false">
<cfset application.PasswordKey = generateSecretKey("AES")>
</cffunction>
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfset request.PasswordKey = application.PasswordKey />
<cfset request.algorithm = "AES" />
<cfset request.encoding = "hex" />
</cffunction>
Though really you want to have the password key hardcoded in a config file otherwise if you restart your server you won't be able to access any of your passwords ever again...
Disable jsafe. Add -Dcoldfusion.disablejsafe=true to your jvm config.

How to obtain the FINAL redirected URL using Coldfusion

Given a URL like:
before: http://feeds.wsjonline.com/~r/wsj/health/feed/~3/felKuQPa41U/
which redirects eventually to:
after: http://blogs.wsj.com/health/2009/08/14/insurance-salesman-to-obama-why-are-you-vilifying-insurers/
Using Coldfusion, how can I obtain that final (after) URL? I believe CFHTTP will redirect automatically up to 4 times, but I can't find a way to obtain that final redirected URL.
ideas? thxs
Searching Google may help, sometimes. http://www.bennadel.com/blog/934-Ask-Ben-Handling-Redirects-With-ColdFusion-CFHttp.htm
if you get a redirect with cfhttp you have two options. 1) you can follow (as you say, up to 4 of them in a row). You could also handle them manually by not following them and checking the location variable of the result. THe code would be something like this (note that this is psudo-coldfusion, my syntax might be off:
<cfset lastgoodURL = "http://bar.com" />
<cfset foo = false />
<cfloop while="foo eq false">
<cfhttp url="#lastgoodURL#" redirect="false" name="baz" />
<cfif length(baz.responseHeader.Location) eq 0>
<cfbreak />
</cfif>
<cfset lastgoodURL = baz.responseHeader.Location />
</cfloop>

Resources