Protecting Your Website Forms With Serverless CAPTCHA
Click here if you are not a robot
If you put a form on one of your web pages, it is wide open to being spammed by anyone on the internet, for their own nefarious purposes. To reduce the noise, web developers often employ a CAPTCHA widget, which tries to ensure that the form filler is human.
As well as the normal form fields, the user will have to tick a “I am not a robot” tick box. Sometimes, they will be asked to solve a numeric, audio or visual puzzle.
- The form is submitted to our server-side script. We are passed the form data and the data the client-side script got from its call to the CAPTCHA API.
- The server side script calls the CAPTCHA API with our server-side ID, the client IP address, and the data we were passed. If the data checks out, we are good to accept the form submission.
CAPTCHA on static websites
My personal homepage, which some say is the fastest website in the world, is a static site built with Jekyll and hosted on GitHub pages. If I’m going to have a contact form, protected by CAPTCHA, then I’m going to need some server-side scripting.
For this we’re going to use IBM Cloud Functions, IBM’s serverless platform, but first we’re going to need to sign up for a CAPTCHA service.
How to Build the Fastest Website in the World
From the Amp project and Google’s Lighthouse, to Offline First and service workers
Sign up for reCAPTCHA
First, sign up for the ReCAPTCHA service, entering your site’s domain name. (Don’t forget to add “localhost” as well as your production domain name if you’re going to develop locally!) You will be supplied with two keys: one for your client-site HTML and the other for your server-side script.
Build a form
Our form is as simple as can be. Just a plain HTML form with a submit button.
You’ll need to replace
RECAPTCHA_KEY with your ReCAPTCHA service's client-side key. We don't know the form's action URL yet. That comes next.
Cloud Functions code
When working with a serverless platform, your code can be nice and simple because the platform handles almost everything for you — you only need to write the code that will execute when each event (in this case a form fill) occurs.
Here’s our complete code:
Its job is to:
- Collect the caller’s IP address, the
g-recaptcha-responsefield from the submitted form and the ReCAPTCHA secret and send it to the ReCAPTCHA API.
- If this request succeeds we can trust the data in the form and bounce the user to a static success page.
- If the request fails, we can ignore the form submission and bounce the user to a static failure page.
Assuming I have the bx wsk installed and configured, then deploying the code is easy:
This is the URL that goes into the action attribute of your HTML form. That’s it!
Make your serverless script do something
If you’ve been paying attention, you may have noticed that the script, once it’s verified that the CAPTCHA submission is valid, doesn’t actually do anything useful with the form data. That’s easily fixed. You could:
- Call a Slack Incoming Webhook to make a message appear on your Slack channel.
- Write a document to a Cloudant database to save your form data for posterity.
- Call the Twilio API to send a text message to your phone.
It’s up to you!
You don’t need to use a serverless platform to handle your form submissions, but it does have several advantages in running your own web service:
- You don’t have to pay to run a server 24x7 that you have to manage. Just upload your code to the serverless platform, and it will invoke your code when it’s needed.
- IBM Cloud Functions has a generous free tier. If you’re not expecting many form fills, then you may not have to pay anything.
- Your code is smaller because you don’t need to code the web server, routing or queuing — only the code that does the work.
- Scaling is the platform’s responsibility. If a burst of thousands of form fills arrive in a short space of time, the platform will scale up to cope with the demand.
If you want to see this in action, visit my homepage, click on the contact button, and fill in my form. I should get your message in my Slack channel!