Pete Freitag Pete Freitag

Adding CloudFlare Turnstile CAPTCHAs to CFML Sites

Updated: October 07, 2022
coldfusion

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.


Like this? Follow me ↯

Adding CloudFlare Turnstile CAPTCHAs to CFML Sites was first published on October 07, 2022.


Fixinator

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

CFBreak
The weekly newsletter for the CFML Community


Comments

Cool stuff, Pete. Thanks for sharing it! I'd seen the news of turnstile (Cloudflare is adding stuff ALL the time, of course), and I'd wondered about implementing it in CFML. Then there you go, doing it for us! :-) You're such a great resource for the community, along with so many other folks. We're indeed blessed.
by Charlie Arehart on 10/07/2022 at 5:22:54 PM UTC

Post a Comment