Skip to content

Implementing Invisible Captcha

Introduction

The concept of an “invisible Captcha” aims to enhance user experience by distinguishing between humans and bots without disrupting user interaction. However, many solutions claiming invisibility still necessitate some form of user engagement, particularly when the system flags suspicious activity.

ALTCHA’s Approach

ALTCHA introduces a proof-of-mechanism (PoW) solution that eliminates the need for direct user interaction, offering a more seamless experience. While this method requires time to solve challenges, it prioritizes user experience by providing notifications during the verification process.

To make the UX even smoother, the ALTCHA widget includes an auto configuration property, enabling automatic initiation of the verification process upon page load, without user intervention.

However, there are other options available that offer an even more frictionless user experience without any client-side widgets.

Option 1 - Invisible PoW

The first option to create a truly invisible Captcha is to leverage the same PoW mechanism used in ALTCHA’s widget, but implement it without the widget, solving the PoW challenge transparently in the background.

For this purpose, utilize the altcha-lib package, which runs in any modern JavaScript environment, including modern browsers. The actual implementation depends on your use case. For example, with forms, you can leverage the M2M mechanism. To see this in action, try generating a Free API Key here. The component uses the M2M ALTCHA mechanism and transparently solves the received challenge before returning a new API key.

Option 2 - Frictionless Spam Detection

In this tutorial, we’ll explore another alternative approach using ALTCHA’s Spam Filter, focusing on server-side implementation to classify form submissions and detect spam. This method seamlessly integrates into the server environment without requiring widgets or client-side Captchas, ensuring a frictionless user experience.

Prerequisites

Before proceeding, ensure you have the following:

  • Bun - Follow the instructions on the official website to install.
  • Text editor - Recommended: Visual Studio Code.
  • Terminal - Command-line interface (CLI).

Setting Up a New Hono Project

We’ll utilize the Hono framework with the Bun runtime for this example. Begin by creating a new project using the bun create command:

Terminal window
bun create hono invisible-captcha

Navigate to the newly created invisible-captcha directory and install dependencies:

Terminal window
cd invisible-captcha
bun install

Setting Up the API

The starter template generates a file src/index.ts. Update its contents as follows:

src/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export default app

To start the development server, execute bun run dev in the terminal:

Terminal window
bun run dev

Access http://localhost:3000 in your browser to ensure the basic HTTP API is operational.

Implementing Form Submission Endpoint

Add a POST /submit route to src/index.ts to handle form submissions:

src/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
app.post('/submit', async (c) => {
const body = await c.req.parseBody()
return c.json({
success: true,
})
})
export default app

This endpoint will receive form data submissions. Test it using an HTTP client like Bruno or Postman.

Integrating the Spam Filter

To access the API, obtain a new API key, which you can create for free.

Upon form submission to the POST /submit endpoint, implement a spam check before processing the data:

src/index.ts
// ... remainder of the file
const API_KEY = 'ckey_...'
const REFERRER = 'https://localhost/'
async function makeSpamFilterRequest(
body: {
email?: string,
fields?: Record<string, string>,
ipAddress?: string
}
) {
const resp = await fetch(`https://eu.altcha.org/api/v1/classify?apiKey=${API_KEY}`, {
body: JSON.stringify(body),
headers: {
'content-type': 'application/json',
'referer': REFERRER
},
method: 'POST',
})
if (resp.status !== 200) {
throw new Error('Unexpected server response')
}
return resp.json()
}
app.post('/submit', async (c) => {
const ipAddress = c.req.header('x-forwarded-for')?.split(',')[0]
const body = await c.req.parseBody()
const { classification } = await makeSpamFilterRequest({
email: String(body.email),
fields: {
name: String(body.name),
message: String(body.message),
},
ipAddress,
})
if (classification === 'BAD') {
return c.json({
error: 'Cannot submit spam.',
success: false,
}, 400)
} else {
// Process the data, send to email, etc.
}
return c.json({
success: true,
})
})

This code snippet performs a spam classification check on the submitted data. If classified as spam, an error is returned; otherwise, the data is processed further.

Conclusion

Implementing the Spam Filter provides a seamless, invisible spam protection solution, eliminating the need for traditional Captchas. For more information, refer to the Spam Filter documentation.

For a complete working example, refer to the repository.

Tips for Enhancing API Security

Email Verification

The Spam Filter can verify email addresses in addition to text fields. Submit the email field along with the optional user’s IP address for validation:

const response = await makeSpamFilterRequest({
email,
ipAddress,
})

The API response includes email validation rules, aiding in distinguishing between work and free email providers and validating email address legitimacy.

Geofencing

Utilize the Spam Filter for geolocation and geofencing by submitting the user’s IP address or time-zone. The API response includes geographic information, enabling region-specific access restrictions.