securing website with google recaptcha
user profile avatar
Tech Wizard

Published on • 🕑3 min read

How to Secure Your Website with Google Recaptcha

1likes3

Blog views976

Listen to this blog

If you have contact, registration or other forms on your website it's important to try and ensure that they are only used by humans. Robots (or 'bots') search out websites with unprotected forms - to either spam the site owner, or worse, potentially send spam to contacts registered with the site.

Google reCAPTCHA can help to stop this - either via a challenge that a human needs to complete (reCAPTCHA v2) or by monitoring the use of the site to establish human usage by score (reCAPTCHA v3). v3 reCAPTCHA is potentially less invasive as a result of its score based system - most valid human users will never see a challenge.

For this tutorial, i am going to focus on reCAPTCHA v3, which works in the background. To get started, head over to google cloud and create a reCAPTCHA key for your site and make sure to choose V3.

 

Google gives you a site key that should be included in your website and client secret that you can use to verify users. Create a .env file and add the keys.

//.env
NEXT_PUBLIC_GOOGLE_RECAPTCHA_SITE_KEY="YOUR_SITE_KEY"
GOOGLE_RECAPTCHA_CLIENT_SECRET="YOUR_CLIENT_SECRET"

Note that i named the site key using NEXT_PUBLIC since browsers require access to this key. However, I do not know if this is the best practice. To get started with recaptcha if you are using react or nextjs, you need to install react-google-recaptcha-v3. 

npm i react-google-recaptcha-v3

For this project, i wanted to secure my login, registration and password reset pages to ensure they do not allow bot traffic. Therefore, i added the GoogleReCaptchaProvider in the layout of my auth pages.

//create a google context provider in .providers/google
"use client";
import { GoogleReCaptchaProvider } from "react-google-recaptcha-v3";
export function GoogleContextProviders({ children }) {
  return (
      <GoogleReCaptchaProvider
        reCaptchaKey={process.env.NEXT_PUBLIC_GOOGLE_RECAPTCHA_SITE_KEY}>
        {children}
      </GoogleReCaptchaProvider>
  );
}
//add the provider to the auth layout
import "../globals.css";
import { GoogleContextProviders } from "@/providers/google";
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <main>
          <GoogleContextProviders>{children}</GoogleContextProviders>
        </main>
      </body>
    </html>
  );
}

After adding the context provider in our routes, we need to initialize google recaptcha in forms that we want to protect. We do this by setting a token state that is submitted when the form is filled, and then verifying the score. Here is a sample login page:

"use client";
import { useState } from "react";
import toast from "react-hot-toast";
import { useGoogleLogin } from "@react-oauth/google";
import { validateRecaptcha } from "@/lib/actions";
export default function LoginPage() {
  const [token, setToken] = useState(false);
  const [formData, setformData] = useState({
            email: "",
            password: "",
  });
  async function handleSubmit(e) {
    e.preventDefault();
    if (!token) {
      toast.error("Kindly complete the recaptcha challenge");
      return;
    }
    const isValid = await validateRecaptcha(token);
    if (isValid) {
      handleLogin(formData);
    } else {
      toast.error("Recaptcha Validation Failed");
    }
 }
  return (
    <form  onSubmit={handleSubmit}>
          </div>
              {/* Add your inputs here */}
          </div>
            <GoogleReCaptcha onVerify={(token) => {  setToken(token) }}  />
            <button type="submit" title="login">
             Login
            </button>  
    </form>
  );
}

Next we need to create a helper function that will be called to validate the recaptcha challenge. Since v3 works in the background, users are not required to prove they are a robot unless required.

//lib.actions
export async function validateRecaptcha(captcha: string) {
  const secretKey = process.env.GOOGLE_RECAPTCHA_CLIENT_SECRET;
  if (!secretKey) {
    throw new Error("Missing Google reCAPTCHA client secret");
  }
  if (!captcha) {
    throw new Error("Missing reCAPTCHA response token");
  }
  const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${captcha}`;
  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
  });
  if (!response.ok) {
    throw new Error(
      `Failed to validate reCAPTCHA response: ${response.statusText}`
    );
  }
  const data = await response.json();
  if (!data.success && data.score < 0.5) {
    throw new Error("Failed reCAPTCHA validation", data.score);
  }
  return true;
}

The validatecaptcha checks the captcha each time the form is submitted and throws an error if the token is absent. In the function we are checking if the user score is above five, meaning they might be a legit user. However, you might choose to be more strict and maybe use a score of 7, which is the average score a normal user would get. 

The final login page looks like this, with the recaptcha protection:

However, Google allows up to 10,000 assessments per month for free, but users need to enable billing if they want to exceed that limit or access additional features. A free alternative is cloudflare turnstile, which claims not to harvest user data as google does.

Resources:

  1. https://cloud.google.com/security/products/recaptcha?hl=en
  2. How to implement turnstile: https://medium.com/designly/a-complete-guide-to-authentication-in-next-js-14-421ec8906854
  3. Recaptcha documentation: https://cloud.google.com/recaptcha/docs

Like what you see? Share with a Friend

1 Comments

3 Likes

Comments (1)

sort comments

Before you comment please read our community guidelines


Please Login or Register to comment

user profile avatar

the don

online

Published on

Capture recaptcha method 😂😂✅✔️