Adding CloudFlare Turnstile CAPTCHAs to CFML Sites
By Pete Freitag
CloudFlare recently released a new CAPTCHA service called Turnstile, which aims to provide a better user experience for CAPTCHA's. At the worst case the user will have to click a checkbox, rather than train a machine learning model solving a puzzle.
You don't need to use CloudFlare's CDN / dynamic proxy services on your site to use this service, and it is free to use.
Client Side Implementation
There are a few different ways to implement the front end side, but at a minimum you would just add these two lines to an existing form:
<div class="cf-turnstile" data-sitekey="YOUR-SITE-KEY"></div> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
Make sure you replace YOUR-SITE-KEY
with Site Key that CloudFlare provides to you.
The JavaScript will automatically insert a hidden input field inside the div named cf-turnstile-response
. When the captcha is solved it will also automatically fill value of the hidden field with the captcha response code:
<input type="hidden" name="cf-turnstile-response" value="automatically filled and generated">
Server Side Implementation
Now your job on the server side is to verify that the value passed in cf-turnstile-response
is legit. On the form action page you will need to make a HTTP request to cloudflare with your secret key and the cf-turnstile-response
value.
Here's a ColdFusion / CFML function I've implemented that takes care of it all:
function verifyTurnstileResponse(secret, response, remoteip="") { try { var httpResult = ""; cfhttp(url="https://challenges.cloudflare.com/turnstile/v0/siteverify", method="POST", result="httpResult", timeout=5) { cfhttpparam(name="secret", value=arguments.secret, type="formfield"); cfhttpparam(name="response", value=arguments.response, type="formfield"); if (len(arguments.remoteip)) { cfhttpparam(name="remoteip", value=arguments.remoteip, type="formfield"); } } if (isJSON(httpResult.fileContent)) { return deserializeJSON(httpResult.fileContent); } else { return { "success": false, "error-codes":["response-was-not-json"], "http-result": httpResult}; } } catch (any err) { return { "success": false, "error-codes":["exception"], "exception": err}; } }
The function should always return a struct with the boolean key "success", if the HTTP request fails for any reason it will return with a success value of false.
Here's how you might use it...
var turnstile = verifyTurnstileResponse(secret=server.system.environment.TURNSTILE_SECRET, response=form["cf-turnstile-response"]); if (turnstile.success) { //do it } else { //log / display error }
The remoteip
argument is optional, so I left it out in my example. I didn't want to default it to cgi.remote_addr
because in some proxied environments that wouldn't be the correct IP.
I'll probably turn this code into a ColdBox module at some point as well.
Adding CloudFlare Turnstile CAPTCHAs to CFML Sites was first published on October 07, 2022.
The Fixinator Code Security Scanner for ColdFusion & CFML is an easy to use security tool that every CF developer can use. It can also easily integrate into CI for automatic scanning on every commit.
Try Fixinator