Skip to content

Cloudflare Turnstile

Sherman K edited this page Aug 19, 2023 · 3 revisions

Turnstile is Cloudflare’s smart CAPTCHA alternative. It can be embedded into any website without sending traffic through Cloudflare and works without showing visitors a CAPTCHA.

In order to have reCAPTCHA gem use Turnstile, you will need to have PR#431 merged, or simply monkey-patch the verification to use POST. Reason being that the server-side verification call must be done via HTTP POST instead of GET: https://developers.cloudflare.com/turnstile/migration/migrating-from-recaptcha/

  1. Ensuring recaptcha gem sends server-side verification via POST:

Assuming PR#431 has not been merged:

  • Create lib/recaptcha.rb:
# Monkey-patching to enable verifications to be sent via POST
module Recaptcha
  def self.api_verification_free(verify_hash, timeout: nil)
    uri = URI.parse(configuration.verify_url)
    http_instance = http_client_for(uri: uri, timeout: timeout)
    request = Net::HTTP::Post.new(uri.request_uri)
    request['Content-Type'] = 'application/json; charset=utf-8'
    request.body = JSON.generate(verify_hash)
    JSON.parse(http_instance.request(request).body)
  end
end
  • Load it in your initializers, e.g. config/initializers/monkey_patches.rb:
require Rails.root.join('lib/recaptcha.rb')
  1. Update your config to use the API Servers
# config/initializers/recaptcha.rb
Recaptcha.configure do |config|
  # Cloudflare Turnstile
  config.verify_url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'
  config.api_server_url = 'https://challenges.cloudflare.com/turnstile/v0/api.js'
end

(Note that we do not include the compatibility layer in the verify_url - i.e. no ?compat=recaptcha as we will be directly using the new params)

  1. Turnstile only supports v2-style tags. Remove any v3-style tags and rely purely on the v2 tags
  • Force the addition of the cf-turnstile classname on the recaptcha tag:
recaptcha_tags class: 'cf-turnstile'
  • If PR#461 has been merged, you need to add the json: true option to ensure that we send the data via JSON and POST:
recaptcha_tags class: 'cf-turnstile', json: true
  • In addition, although "actions" verification is only available in reCAPTCHA v3 service, Turnstile actually supports it with v2. However, you must supply them as data-attributes on the recaptcha tag as data-action:
recaptcha_tags class: 'cf-turnstile', json: true, 'data-action': 'contact_us'
  1. On the backend, you need to validate with the server verification, but you will need to specify where the params are from (cf-turnstile-response)
  • Add response param:
verify_recaptcha response: params['cf-turnstile-response']
  • Add request data (IP and Hostname):
verify_recaptcha response: params['cf-turnstile-response'], remoteip: request.ip, hostname: request.hostname
  • Add action verification:
verify_recaptcha response: params['cf-turnstile-response'], remoteip: request.ip, hostname: request.hostname, action: 'contact_us'
  1. Voila! After calling verify_recaptcha, the server's response will be available in recaptcha_reply method in the controller, as if this were a v3 call. The response body does not contain a score param like in v3 however, but instead has all the information you need in terms of timestamp, hostname, action, and cdata.

Example recaptcha_reply:

{
  action: "messages",  
  cdata: "", 
  challenge_ts: "2023-08-19T03:38:03.777Z", 
  error-codes: [], 
  hostname: "127.0.0.1", 
  metadata: {
    interactive: false
  }, 
  success: true
}
  • If you provided data-cdata in the recaptcha_tags, it will be returned in recaptcha_reply[:cdata]

Need to customise your widget to stop respecting dark mode? Want to shrink that checkbox? See the recaptcha_tag configuration options (via data-attributes) available at https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#configurations