A CFML developer in the ColdFusion Programmers Facebook Group referenced my DEV article regarding how to identify the SSL expiration date using ColdFusion as they were encountering some issues and thought it may be due to the version of Java that was being using.
Here's a UDF that I wrote a couple years ago that leverages CURL (open source) to fetch & identify the current SSL certificate data in use.
I've added a resolveIp
option so that you can override DNS lookup and bind the hostname to any IP so that both origin & WAF IP addresses can be tested. (Some web applications often have more than 1 IP address and both certificates should to be tested.)
There's also a useragent
option to pass a custom user agent in case you require something specific to bypass security restrictions. (I recommend perform requests for static robots.txt
files. Even if robots.txt doesn't exist, the "404 Not Found" message will return the error using the SSL connection and this is all that really matters.)
BTW: If you're going to CFSummit2023 in Las Vegas, come find me. I enjoy meeting other CFDevs. (The person that I usually talk about tech with is my .NET brother.)
result = checkSSLCertificate("https://www.adobe.com/robots.txt");
writedump(result);
ColdFusion/CFML Source Code
Enjoy!
https://gist.github.com/JamoCA/fa7449d1f1a8b920d901b9b14a773e96
<!--- checkSSLCertificate UDF - I use ColdFusion & CURL to connect to remote HOST to identify SSL data (start/end dates, subject, subjectAltName, issuer & status) #cfml | |
GIST: https://gist.github.com/JamoCA/fa7449d1f1a8b920d901b9b14a773e96 | |
BLOG: https://dev.to/gamesover/how-to-check-ssl-certificate-using-coldfusion-curlexe-2c92 | |
TWITTER: https://twitter.com/gamesover/status/1707506769466216593 | |
NOTE: This UDF requires CURL. https://curl.se/ | |
---> | |
<cfscript> | |
struct function checkSSLCertificate(required string targetUrl, string userAgent="", string resolveIp="", string exePath="", boolean debug=false) output=false hint="I use CURL to connect to remote HOST to identify SSL data (start/end dates, subject, subjectAltName, issuer & status)" { | |
arguments.exePath = (len(arguments.exePath)) ? arguments.exePath : "C:\CURL\CURL.exe"; // set to default CURL exe path | |
arguments.useAgent = (len(arguments.userAgent)) ? arguments.userAgent : "FireFox 13|Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0.1"; | |
local.errorCodes = ["1": "UNSUPPORTED_PROTOCOL", "2": "FAILED_INIT", "3": "URL_MALFORMAT", "4": "NOT_BUILT_IN", "5": "COULDNT_RESOLVE_PROXY", "6": "COULDNT_RESOLVE_HOST", "7": "COULDNT_CONNECT", "8": "FTP_WEIRD_SERVER_REPLY", "9": "REMOTE_ACCESS_DENIE", "10": "FTP_ACCEPT_FAILED", "11": "FTP_WEIRD_PASS_REPLY", "12": "FTP_ACCEPT_TIMEOUT", "13": "FTP_WEIRD_PASV_REPLY", "14": "FTP_WEIRD_227_FORMAT", "15": "FTP_CANT_GET_HOST", "16": "HTTP2", "17": "FTP_COULDNT_SET_TYPE", "18": "PARTIAL_FILE", "19": "FTP_COULDNT_RETR_FILE", "21": "QUOTE_ERROR", "22": "HTTP_RETURNED_ERROR", "23": "WRITE_ERROR", "25": "UPLOAD_FAILED", "26": "READ_ERROR", "27": "OUT_OF_MEMORY", "28": "OPERATION_TIMEDOUT", "30": "FTP_PORT_FAILED", "31": "FTP_COULDNT_USE_REST", "33": "RANGE_ERROR", "34": "HTTP_POST_ERROR", "35": "SSL_CONNECT_ERROR", "36": "BAD_DOWNLOAD_RESUME", "37": "FILE_COULDNT_READ_FILE", "38": "LDAP_CANNOT_BIND", "39": "LDAP_SEARCH_FAILED", "41": "FUNCTION_NOT_FOUND", "42": "ABORTED_BY_CALLBACK", "43": "BAD_FUNCTION_ARGUMENT", "45": "INTERFACE_FAILED", "47": "TOO_MANY_REDIRECTS", "48": "UNKNOWN_OPTION", "49": "TELNET_OPTION_SYNTAX", "51": "PEER_FAILED_VERIFICATION", "52": "GOT_NOTHING", "53": "SSL_ENGINE_NOTFOUND", "54": "SSL_ENGINE_SETFAILED", "55": "SEND_ERROR", "56": "RECV_ERROR", "58": "SSL_CERTPROBLEM", "59": "SSL_CIPHER", "60": "SSL_CACERT", "61": "BAD_CONTENT_ENCODING", "62": "LDAP_INVALID_URL", "63": "FILESIZE_EXCEEDED", "64": "USE_SSL_FAILED", "65": "SEND_FAIL_REWIND", "66": "SSL_ENGINE_INITFAILED", "67": "LOGIN_DENIED", "68": "TFTP_NOTFOUND", "69": "TFTP_PERM", "70": "REMOTE_DISK_FULL", "71": "TFTP_ILLEGAL", "72": "TFTP_UNKNOWNID", "73": "REMOTE_FILE_EXISTS", "74": "TFTP_NOSUCHUSER", "75": "CONV_FAILED", "76": "CONV_REQD", "77": "SSL_CACERT_BADFILE", "78": "REMOTE_FILE_NOT_FOUND", "79": "SSH", "80": "SSL_SHUTDOWN_FAILED", "81": "AGAIN", "82": "SSL_CRL_BADFILE", "83": "SSL_ISSUER_ERROR", "84": "FTP_PRET_FAILED", "85": "RTSP_CSEQ_ERROR", "86": "RTSP_SESSION_ERROR", "87": "FTP_BAD_FILE_LIST", "88": "CHUNK_FAILED", "89": "NO_CONNECTION_AVAILABLE", "90": "SSL_PINNEDPUBKEYNOTMATCH", "91": "SSL_INVALIDCERTSTATUS", "92": "HTTP2_STREAM", "93": "RECURSIVE_API_CALL", "94": "AUTH_ERROR", "95": "HTTP3", "96": "QUIC_CONNECT_ERROR"]; | |
local.result = [ | |
"ssl": [:] | |
,"duration": javacast("int", 0) | |
,"headers": [ | |
"ip": "0.0.0.0" | |
,"status": javacast("int", 0) | |
] | |
,"args": [ | |
"arguments": arguments | |
,"params": "-s -verbose" | |
,"isValidURL": isvalid("url", arguments.targetUrl) | |
] | |
,"raw": "" | |
]; | |
if (len(arguments.resolveIp) && refindnocase("^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)", arguments.resolveIp)) { | |
local.port = "80"; | |
local.hostname = listgetat(arguments.targetUrl, 2, "/"); | |
if (findnocase("https://", arguments.targetUrl)) { | |
local.port = "443"; | |
} | |
local.result.args.params = local.result.args.params & " --resolve #local.hostname#:#local.port#:#arguments.resolveIp#"; | |
} | |
local.result.args.params = local.result.args.params & " -A ""#arguments.userAgent#"" #listfirst(arguments.targetUrl,'?')#"; | |
if (find("?", arguments.targetUrl)) { | |
local.result.args.params = local.result.args.params & " -d """ & listrest(arguments.targetUrl, "?") & """"; | |
} | |
local.result.args.params = local.result.args.params & " --stderr - 2>&1"; | |
local.timeStart = gettickcount(); | |
if (local.result.args.isValidURL) { | |
cfexecute(arguments=local.result.args.params, variable="local.result.Raw", name=arguments.exePath, timeout=15); | |
local.result.duration = javacast("int", gettickcount() - local.timeStart); | |
for (local.thisLine in listtoarray(local.result.Raw, chr(13) & chr(10))) { | |
if (left(trim(local.thisLine), 2) eq "< " && listlen(trim(local.thisLine), ":") gte 2) { | |
local.result.headers["#listrest(trim(listfirst(local.thisLine, ":")), " ")#"] = trim(listrest(local.thisLine, ":")); | |
} else if (left(trim(local.thisLine), 6) eq "< HTTP") { | |
local.result.headers["Status"] = trim(listrest(listrest(trim(local.thisLine), " "), " ")); | |
} else if (left(trim(local.thisLine), 10) eq "* Trying") { | |
local.result.headers["IP"] = trim(listLast(listfirst(trim(local.thisLine), ":"), " ")); | |
} else if (left(trim(local.thisLine), 14) eq "* Connected to") { | |
local.result.headers["IP"] = trim(listrest(listfirst(trim(local.thisLine), ")"), "(")); | |
} else if (left(trim(local.thisLine), 6) eq "curl: ") { | |
if (!local.result.keyExists("error")) { | |
local.result["error"] = [:]; | |
} | |
local.result.error["message"] = trim(listrest(local.thisline, ":")); | |
local.result.error["id"] = javacast("int", 0); | |
local.result.error["code"] = ""; | |
if (find("(", local.result.error.message) && find(")", local.result.error.message)) { | |
local.result.error.id = javacast("int", listfirst(local.result.error.message, " ").replaceAll("\D", "")); | |
local.result.error.message = trim(listrest(local.result.error.message, " ")); | |
if (local.errorCodes.keyExists("#local.result.error.id#")) { | |
local.result.error.code = local.errorCodes["#local.result.error.id#"]; | |
} | |
} | |
} else if (left(trim(local.thisLine), 19) eq "More Details here: ") { | |
if (!local.result.keyExists("error")) { | |
local.result["error"] = [:]; | |
} | |
local.result.error["details"] = trim(listrest(local.thisline, ":")); | |
} else if (left(trim(local.thisLine), 3) eq "* ") { | |
if (listlen(trim(local.thisLine), ":") gte 2) { | |
local.result.ssl["#trim(replace(listrest(trim(listfirst(local.thisLine, ":")), " "), " ", "_", "all"))#"] = trim(listrest(local.thisLine, ":")); | |
} else { | |
local.result.ssl["status"] = trim(listrest(trim(local.thisLine), " ")); | |
} | |
} | |
} | |
local.result.headers.ip = local.result.headers.ip.replaceAll("\.\.\.", ""); | |
for (local.keyname in local.result.ssl) { | |
if (isdate(local.result.ssl["#local.keyname#"].replaceAll("\s\d+:\d+:\d+\s", " ").replaceAll(" GMT", ""))) { | |
local.result.ssl["#local.keyname#"] = dateformat(local.result.ssl["#local.keyname#"].replaceAll("\s\d+:\d+:\d+\s", " ").replaceAll(" GMT", ""), "yyyy-mm-dd"); | |
} | |
} | |
} else { | |
local.result.headers.status = javacast("int", 0); | |
local.result.error = [ | |
"message": "Invalid URL" | |
,"id": javacast("int", 0) | |
,"code": "INVALID_URL" | |
]; | |
} | |
if (!arguments.debug) { | |
structdelete(local.result, "headers"); | |
structdelete(local.result, "args"); | |
structdelete(local.result, "raw"); | |
} | |
return local.result; | |
} | |
</cfscript> | |
<cfset result = CheckSSLCertificate(targetURL="https://www.coldfusion.com/robots.txt", debug=true)> | |
<cfdump var="#result#" label="CheckSSLCertificate UDF Results"> |
Top comments (0)