Anyone that operates a website or web application with a contact page knows how bad the SPAM problem on the internet is. Spammers, phishers, and other malicious people create bots that will crawl search engines for contact forms and send emails to the hosts, register accounts, or something else. There was a point in time that I was receiving more than ten emails a day from spammers that wanted to redesign my website. This kind of activity is incredibly annoying on my inbox.
This is where CAPTCHA and reCAPTCHA form elements come into play. These elements typically require some interaction such as typing words from an image or solving a math problem. Adding this small amount of form complexity can go a long way towards stopping bots.
We’re going to see how to hook up a Google reCAPTCHA element into a client facing form and validate that element using a Node.js backend.
We’re going to be creating a fresh client facing and server side application to keep this example easy to understand. We won’t be using any special frameworks beyond Bootstrap and Express.
Depending on what the reCAPTCHA decides, is what your users will have to accomplish to prove they are human. It could be as easy as checking a box, or as complicated as choosing from a list of pictures.
Like with any Google service, you need to register your project or web application before you can start using it. While not part of the Google Cloud Portal like many of Google’s other services, the reCAPTCHA service follows similar steps.
The first step is to sign into the dashboard for the Google reCAPTCHA service.
After signed in, you can register a new site. You’ll want to use reCAPTCHA V2 for this particular project.
With the site registered, you’ll need to define which domains are allowed to work with it as well as the domain owners. If you plan to test locally, you’ll need localhost
to be one of the available domains.
When you’re done, you’ll have access to two different keys. The secret key is to be used in the Node.js application and the site key is to be used in the client facing frontend.
Now we can focus on development!
The backend is responsible for validating the form and ultimately performing some task after we are confirmed to be validated. This could be along the lines of registering a user, sending an email, or anything else that might need protection against bots.
Assuming that you’ve already installed and configured Node.js, execute the following command via a Command Prompt or Terminal:
npm init --y
The above command will create a package.json file for holding our application dependencies. The following command will install said dependencies:
npm install body-parser express request --save
We’ll be using express
for our API framework, body-parser
for accepting POST body data such as form data, and request
for issuing POST requests from within Node.js.
Create an app.js file and include the following JavaScript code:
var Express = require("express");
var BodyParser = require("body-parser");
var Request = require("request");
var app = Express();
app.use(BodyParser.json());
app.use(BodyParser.urlencoded({ extended: true }));
const RECAPTCHA_SECRET = "SECRET_KEY_HERE";
app.post("/email", function(request, response) {
var recaptcha_url = "https://www.google.com/recaptcha/api/siteverify?";
recaptcha_url += "secret=" + RECAPTCHA_SECRET + "&";
recaptcha_url += "response=" + request.body["g-recaptcha-response"] + "&";
recaptcha_url += "remoteip=" + request.connection.remoteAddress;
Request(recaptcha_url, function(error, resp, body) {
body = JSON.parse(body);
if(body.success !== undefined && !body.success) {
return response.send({ "message": "Captcha validation failed" });
}
response.header("Content-Type", "application/json").send(body);
});
});
var server = app.listen(3000, function() {
console.log("Listening on port " + server.address().port + "...");
});
So what is happening in the above code?
First we’re importing the dependencies that were previously downloaded. After importing the dependencies, we’re initializing Express and configuring it to accept POST body data.
Within Google, you should have been provided two different keys. Grab the secret key and include it in the Node.js application. Finally we have an API endpoint that our client will hit and code to start serving the application.
app.post("/email", function(request, response) {
var recaptcha_url = "https://www.google.com/recaptcha/api/siteverify?";
recaptcha_url += "secret=" + RECAPTCHA_SECRET + "&";
recaptcha_url += "response=" + request.body["g-recaptcha-response"] + "&";
recaptcha_url += "remoteip=" + request.connection.remoteAddress;
Request(recaptcha_url, function(error, resp, body) {
body = JSON.parse(body);
if(body.success !== undefined && !body.success) {
return response.send({ "message": "Captcha validation failed" });
}
response.header("Content-Type", "application/json").send(body);
});
});
In the above endpoint we construct a URL based on the instructions in the Google dashboard. This URL consists of the secret, the value constructed from the reCAPTCHA in the client, and the requesting IP address. With the constructed URL in hand, we can issue a POST request. If the request is successful, meaning the reCAPTCHA was valid, the response body will have a property success
that is set to true. If we are not successful, throw some kind of error to the client, otherwise just spit out the response.
You can always add your own logic around this such as validating the form fields that were passed in the request.
With the reCAPTCHA service ready and the validation backend in place, we can create a simple form that accepts user data. Instead of just throwing some unstyled elements on the screen we are going to use simple Bootstrap. The form itself isn’t too important, but instead the reCAPTCHA itself.
Take a look at the following HTML:
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" />
<script src="https://www.google.com/recaptcha/api.js"></script>
</head>
<body>
<div class="container" style="margin-top: 20px">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Send Feedback</h3>
</div>
<div class="panel-body">
<form method="post" action="http://localhost:3000/email">
<div class="form-group">
<label for="sender">From:</label>
<input id="sender" name="sender" type="email" class="form-control" />
</div>
<div class="form-group">
<label for="subject">Subject:</label>
<input id="subject" name="subject" type="text" class="form-control" />
</div>
<div class="form-group">
<label for="message">Message:</label>
<textarea id="message" name="message" class="form-control" style="height: 200px"></textarea>
</div>
<div class="g-recaptcha" data-sitekey="WEBSITE_KEY_HERE"></div>
<br />
<button type="submit" class="btn btn-default">Send</button>
</form>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</body>
</html>
Ignoring all the boilerplate markup in the above, we have a simple form. When submitted, it will issue a POST request to our Node.js backend.
The reCAPTCHA is actually generated through the following line:
<div class="g-recaptcha" data-sitekey="WEBSITE_KEY_HERE"></div>
Once you provide your website key, it will be generated using the JavaScript that we’ve included through the following line:
<script src="https://www.google.com/recaptcha/api.js"></script>
At this point you can demo the application as long as the backend is functioning. Regardless if the backend is functioning, the reCAPTCHA will still display and function, we just won’t be able to validate it when the form was submitted.
You just saw how to add Google reCAPTCHA support to your website or web application which will help deter spammers and other bots from using your forms. The application logic used here can be taken to other languages beyond Node.js because we’re only doing simple POST requests.
Interested in downloading the full project? You can download both the client and server code here.