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.
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.