DIY reCAPTCHA with Ruby
Tags: ruby, rails, googlerecaptcha, captcha
Published: 2017-10-19 14:23:34

---
title: DIY reCAPTCHA with Ruby
published: true
description: In this post, we'll implement Google's reCAPTCHA protocol using the Google reCAPTCHA 2 client-side library and our very own hand-rolled verification client.
tags: Ruby, Rails, Google reCAPTCHA, CAPTCHA
---
*This post was originally published on my personal blog, [The Great Code Adventure](http://www.thegreatcodeadventure.com/google-recaptcha-ruby/).*

reCAPTCHA is an anti-bot tool you can implement in your web app to prevent bad actors from programmatically filling out forms and spamming your endpoints. In this post, we'll implement Google's reCAPTCHA protocol using the Google reCAPTCHA 2 client-side library and our very own hand-rolled verification client.

## I am Not a Robot

[CAPTCHA](http://www.captcha.net/) is a mechanism through which we can programmatically determine whether a user is a human or a bot. CAPTCHA actually stands for "Completely Automated Public Turing test to tell Computers and Humans Apart". A typical CAPTCHA will ask the user to submit some input that only a human (or a Cylon? That part is still a little fuzzy...) can provide. For example, typing in some blurry letters from an image, selected a set of images that contain a car or a street sign.

![](https://thepracticaldev.s3.amazonaws.com/i/x1ihlac2rj9yexuxjp2q.png)<span style="font-size: 10px">[source](https://cdn.shopify.com/s/files/1/0289/1534/products/QMx_BSG_Spot_a_Cylon-2_988X988_60ecb559-caf0-40e2-9e9b-45478b3797b2.jpg?v=1401820189)</span>

Captcha has many applications--preventing spam comments, stopping bots from registering for your app, and fighting DDoS attacks in the form of bot-driven logins, to name a few. 


## Google reCAPTCHA 2

Google offers a relatively new CAPTCHA protocol--"reCAPTCHA"––which allows users to verify that they are not robots with the simple click of a button. No need to "solve" a traditional CAPTCHA by typing in words or identifying pictures.  

Integrating Google reCAPTCHA is pretty straightforward. First thing's first though, we need to register our app with the Google reCAPTCHA service and receive our application's site key and secret key. 

1. Log in to your Google account and visit https://www.google.com/recaptcha/intro/, click the "Get reCAPTCHA" button on the right hand side.
2. On the next page, select "reCAPTCHA 2" from the "type of reCAPTCHA" list and enter your application's domain. 
3. Lastly, grab your site key and secret key from the "keys" section of the next page. Note the client-side and server-side instructions. We'll use these guides to implement reCAPTCHA on our site. 

## Implementing reCAPTCHA on the Client-Side

We'll be using reCAPTCHA for the sign up page of our Rails application. We want our users to verify that they are not bots before submitting the sign up form. 

First, we need to load the reCAPTCHA library. We'll do so in the application layout, so that it's available on any page we want to use it. 

```
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
  <head>
    ...
    <script src='https://www.google.com/recaptcha/api.js'>
    </script>
  </head>

  <body>
    <%= render partial: 'layouts/navbar' %>
    <div class="container">
      <%= yield %>
    </div>
  </body>
</html>
```

There are two ways to include reCAPTCHA on a particular page––automatically and explicity. 

To [automatically render](https://developers.google.com/recaptcha/docs/display#auto_render) the reCAPTCHA widget on the page, we simply add the following anywhere on the page:

```
<div class="g-recaptcha" data-sitekey="your_site_key"></div>
```

However, we want a little more control over the appearance and behavior of our widget. For example, we want to set the theme and language of the widget. For this level of control, we'll use the [explicit render approach](https://developers.google.com/recaptcha/docs/display#explicit_render). 

To explicitly render the reCAPTCHA widget, we'll do the following:

1. Define an onload callback function, and insert the reCAPTCHA resource.
2. Leverage the reCAPTCHA `grecaptcha.render` function to configure reCAPTCHA.
3. Define the HTML element on the load of which the reCAPTCHA will render.

 Let's define our onload function and insert the Javascript resource. Note that when we insert the resource, we are setting the `onload` parameter to the name of our onload callback function, `onRecaptchaElementLoad`, and the `render` parameter to `explicit`. Also note that we are using the `async defer` flag for our `script` tag, to ensure that the script is not run before the rest of the page loads.

```
# app/views/registrations/new.html.erb
<%=javascript_tag do %>
  var onRecaptchaElementLoad = function() {
    // coming soon!
  };
<%end%>

<script src='https://www.google.com/recaptcha/api.js?onload=onRecaptchaElementLoad&render=explicit' async defer></script>
```

Now let's define our function body to use the [`grecaptcha.render`](https://developers.google.com/recaptcha/docs/display#js_api) function. This function takes in two arguemtns:

1. The ID of the element whose load will trigger this callback. 
2. A set of options for configuring the reCAPTCHA widget.

```erb
# app/views/registrations/new.html.erb
<%=javascript_tag do %>
  var onRecaptchaElementLoad = function() {
    grecaptcha.render('recaptcha', {
      'sitekey' : '<%= j ENV["REACAPTCHA_SITE_KEY"]%>',
      'hl': '<%= j locale %>',
      'theme': 'dark'
    });
  };
<%end%>
...
```

The `sitekey` param is required, and is set to the value of the site key we received when we registered our app with the Google reCAPTCHA service. I added my site key to my ENV vars with the help of the dotenv gem. 

We also set optional params `hl`, the language designation, and `theme`. This snippet assumes we have a view helper method `locale` which returns a valid language code ("en", "de", "fr", etc). 

Now that we've defined our callback function, let's add our reCAPTCHA element, a `div` with an `id` of "reCAPTCHA", to the page. This element should be added _within_ the registration form.  

```
# app/views/registrations/new.html.erb
<%= form_for @user do |f| %>
  ...
  <div id="recaptcha"></div>
  <%= f.submit %>
<% end %>
```

That's it for our client-side integration. Our form will now submit with the following param:

```ruby
{"g-recaptcha-response" => "<recaptcha code>"}
``` 

Let's move on to our server-side implementation. 

## Server-Side 

We'll verify our reCAPTCHA response as a `before_action` in our controller. Although we are only placing the reCAPTCHA widget on our registration form right now, I absolutely anticipate using reCAPTCHA on other pages. For example, let's say a few weeks from now I find my sign in or password reset pages being spammed by a lot of bot form submission attempts. I want the ability to quickly and easily include reCAPTCHA verification to these and potentially other controllers in the future. And I want to do so without repeating code. 

So, in anticipation of this future requirement, and with the desire to keep our code DRY always in mind, we'll put our reCAPTCHA verification code in a controller module that we'll include in the `RegistrationsController`. 

### Verifying reCAPTCHA in the Controller Module

We'll define our module in `app/controllers/concerns`. It will call a before action which will in turn call on our very own reCAPTCHA verification service (coming soon!). It will redirect if the verification failed. It will leverage `ActiveSupport::Concern` to get access to some nice clean `included; do` syntax.

```ruby
# app/controllers/concerns/recaptcha_verifiable.rb
require 'active_support/concern'

module RecaptchaVerifiable
  extend ActiveSupport::Concern

  included do
    before_filter :recaptcha, only: [:create]
  end

  def recaptcha
    reroute_failed_recaptcha && return unless RecaptchaVerifier.verify(params["g-recaptcha-response"], request.ip)
  end

  def reroute_failed_recaptcha
    @person           = Person.new
    flash.now[:error] = "Please verify you are not a robot."
    render action: "new"
  end
end
```

We'll include this module in our `RegistrationsController`, and we can include it in any controller to which we want to add recaptcha in the future. 

```ruby
# app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
  include RecaptchaVerifiable
  ...
end
```

Now that we've taught our controller how to verify reCAPTCHA, and what to do if that verification fails, let's build out of verification service, `RecaptchaVerifier`.

#### Building our own reCAPTCHA Verifier

We'll define our verifier in `app/services`. Our service will have one responsibility: given a reCAPTCHA response, return `true` if the response is valid, and return `false` if not. 

Let's think about this responsibility for a moment. In order for our verifier service to carry this responsibility, does it _need_ to know about the reCAPTCHA mechanism, i.e. that we are using Google reCAPTCHA? No! 

Our service will use dependency injection to call on a `recaptcha_client`. That client will be responsible to enacting the reCAPTCHA verification via an external API, in our case the Google reCAPTCHA API. 

```ruby
# app/services/recaptcha_verifier.rb
class RecaptchaVerifier
  def self.verify(response, remote_ip, recaptcha_client=GoogleRecaptcha)
    new(response, remote_ip, recaptcha_client).verify
  end

  def initialize(response, remote_ip, recaptcha_client)
    @recaptcha_response = response
    @remote_ip          = remote_ip
    @recaptcha_client   = recaptcha_client.new
  end

  def verify
    return false unless recaptcha_response
    recaptcha_client.verify_recaptcha(response: recaptcha_response, remoteip: remote_ip)
  rescue
    false
  end

  private

  attr_reader :recaptcha_client, :recaptcha_response, :remote_ip
end
```

Here, we're passing in the reCAPTCHA response from the params, the IP address (this is a param that the Google reCAPTCHA API requires, and we'll take a look at that API soon), and a third, optional argument of the reCAPTCHA client. We default that client to the `GoogleRecaptcha`, a class that we will define next to handle the actual Google reCAPTCHA API request. 

Our service is pretty simple. It:

* Returns `false` in the absence of a reCAPTCHA response (this could happen if our controller endpoint gets hit _without_ the `"g-recaptcha"` param––a sure sign of a bad actor)
* Calls on the client to verify the reCAPTCHA response, returning the value of that call, which we assume will be `true` or `false`. 

Now we're ready to build our `GoogleRecaptcha` client. 

#### Building our own Google reCAPTCHA Client

Our Google reCAPTCHA client will act as the actual verification mechanism in our reCAPTCHA flow. As such, it has one job: send the verification request to the Google reCAPTCHA API and parse the response. 

We'll define our client in `lib/`. 

```ruby
# lib/google_recaptcha.rb
class GoogleRecaptcha
  BASE_URL   = "https://www.google.com/".freeze
  VERIFY_URL = "recaptcha/api/siteverify".freeze

  def initialize
    @client = Faraday.new(BASE_URL)
  end

  def verify_recaptcha(params)
    response = perform_verify_request(params)
    success?(response)
  end

  def success?(response)
    JSON.parse(response.body)["success"]
  end

  private

  attr_reader :client

  def perform_verify_request(params)
    client.post(VERIFY_URL) do |req|
      req.params = params.merge({secret: ENV["RECAPTCHA_SECRET_KEY"]})
    end
  end
end
```

Our client expects to receive an argument of a hash containing the Google reCAPTCHA API's required params of `remoteip` (pointing to the IP of the original user's request) and `response` (pointing to the response that came through from the `"g-recaptcha"` param of the original request). 

To these params, we add the key/value pair: `{secret: ENV["RECAPTCHA_SECRET_KEY"]}` (assuming we've set our secret key to an ENV var). 

This allows us to send the [verification request to the Google API's verification endpoint](https://developers.google.com/recaptcha/docs/verify): `https://www.google.com/recaptcha/api/siteverify`

Google will respond to this request with the following:

```json
{
  "success": true|false,
  "challenge_ts": timestamp, 
  "hostname": string,
  "error-codes": [...] 
}
``` 

We will parse the JSON of the response body to return `true` or `false` depending on the value of the `"success"` key. 

And that's it! That allows us to call on our service in our controller `before_action`:

```ruby
# app/controllers/registrations_controller.rb, by way of the RecaptchaVerifiable module

RecaptchaVerifier.verify(recaptcha_response, ip_address)
```

Which will in turn call on our `GoogleRecaptcha` client to return `true` or `false`, depending on the validity of the reCAPTCHA response. 

Your site is now officially robot proof!

![](https://thepracticaldev.s3.amazonaws.com/i/hmadrhlrb76opju749o8.png)
<span style="font-size: 10px">[source](https://wallpapercave.com/wp/CY8JOOV.jpg)</span>